Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Result with exception handling #906

Merged
merged 7 commits into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Remove Result, add exceptions
  • Loading branch information
dturner committed Feb 22, 2023
commit 126558f57e1722a958728bd6d0aa9f86d521b123
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.android.architecture.blueprints.todoapp.R
import com.example.android.architecture.blueprints.todoapp.TodoDestinationsArgs
import com.example.android.architecture.blueprints.todoapp.data.Result.Success
import com.example.android.architecture.blueprints.todoapp.data.Task
import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository
import dagger.hilt.android.lifecycle.HiltViewModel
Expand Down Expand Up @@ -132,9 +131,8 @@ class AddEditTaskViewModel @Inject constructor(
it.copy(isLoading = true)
}
viewModelScope.launch {
tasksRepository.getTask(taskId).let { result ->
if (result is Success) {
val task = result.data
tasksRepository.getTask(taskId).let { task ->
if (task != null) {
_uiState.update {
it.copy(
title = task.title,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

package com.example.android.architecture.blueprints.todoapp.data.source

import com.example.android.architecture.blueprints.todoapp.data.Result
import com.example.android.architecture.blueprints.todoapp.data.Result.Success
import com.example.android.architecture.blueprints.todoapp.data.Task
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
Expand All @@ -35,13 +33,9 @@ class DefaultTasksRepository(
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : TasksRepository {

override suspend fun getTasks(forceUpdate: Boolean): Result<List<Task>> {
override suspend fun getTasks(forceUpdate: Boolean): List<Task> {
if (forceUpdate) {
try {
updateTasksFromRemoteDataSource()
} catch (ex: Exception) {
return Result.Error(ex)
}
updateTasksFromRemoteDataSource()
}
return tasksLocalDataSource.getTasks()
}
Expand All @@ -50,7 +44,7 @@ class DefaultTasksRepository(
updateTasksFromRemoteDataSource()
}

override fun getTasksStream(): Flow<Result<List<Task>>> {
override fun getTasksStream(): Flow<List<Task>> {
return tasksLocalDataSource.getTasksStream()
}

Expand All @@ -61,33 +55,35 @@ class DefaultTasksRepository(
private suspend fun updateTasksFromRemoteDataSource() {
val remoteTasks = tasksRemoteDataSource.getTasks()

if (remoteTasks is Success) {
// Real apps might want to do a proper sync, deleting, modifying or adding each task.
tasksLocalDataSource.deleteAllTasks()
remoteTasks.data.forEach { task ->
tasksLocalDataSource.saveTask(task)
}
} else if (remoteTasks is Result.Error) {
throw remoteTasks.exception
// Real apps might want to do a proper sync, deleting, modifying or adding each task.
tasksLocalDataSource.deleteAllTasks()
remoteTasks.forEach { task ->
tasksLocalDataSource.saveTask(task)
}
}

override fun getTaskStream(taskId: String): Flow<Result<Task>> {
override fun getTaskStream(taskId: String): Flow<Task?> {
return tasksLocalDataSource.getTaskStream(taskId)
}

private suspend fun updateTaskFromRemoteDataSource(taskId: String) {
val remoteTask = tasksRemoteDataSource.getTask(taskId)

if (remoteTask is Success) {
tasksLocalDataSource.saveTask(remoteTask.data)
if (remoteTask == null) {
tasksLocalDataSource.deleteTask(taskId)
} else {
tasksLocalDataSource.saveTask(remoteTask)
}
}

/**
* Relies on [getTasks] to fetch data and picks the task with the same ID.
* Relies on [getTasks] to fetch data and picks the task with the same ID. Will return a null
* Task if the task cannot be found.
*
* @param taskId - The ID of the task
* @param forceUpdate - true if the task should be updated from the remote data source.
*/
override suspend fun getTask(taskId: String, forceUpdate: Boolean): Result<Task> {
override suspend fun getTask(taskId: String, forceUpdate: Boolean): Task? {
if (forceUpdate) {
updateTaskFromRemoteDataSource(taskId)
}
Expand All @@ -110,9 +106,7 @@ class DefaultTasksRepository(

override suspend fun completeTask(taskId: String) {
withContext(ioDispatcher) {
(getTaskWithId(taskId) as? Success)?.let { it ->
completeTask(it.data)
}
getTaskWithId(taskId)?.run { completeTask(this) }
dturner marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -125,9 +119,7 @@ class DefaultTasksRepository(

override suspend fun activateTask(taskId: String) {
withContext(ioDispatcher) {
(getTaskWithId(taskId) as? Success)?.let { it ->
activateTask(it.data)
}
getTaskWithId(taskId)?.run { activateTask(this) }
dturner marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -154,7 +146,7 @@ class DefaultTasksRepository(
}
}

private suspend fun getTaskWithId(id: String): Result<Task> {
private suspend fun getTaskWithId(id: String): Task? {
return tasksLocalDataSource.getTask(id)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package com.example.android.architecture.blueprints.todoapp.data.source

import com.example.android.architecture.blueprints.todoapp.data.Result
import com.example.android.architecture.blueprints.todoapp.data.Task
import kotlinx.coroutines.flow.Flow

Expand All @@ -25,15 +24,15 @@ import kotlinx.coroutines.flow.Flow
*/
interface TasksDataSource {

fun getTasksStream(): Flow<Result<List<Task>>>
fun getTasksStream(): Flow<List<Task>>

suspend fun getTasks(): Result<List<Task>>
suspend fun getTasks(): List<Task>

suspend fun refreshTasks()

fun getTaskStream(taskId: String): Flow<Result<Task>>
fun getTaskStream(taskId: String): Flow<Task?>

suspend fun getTask(taskId: String): Result<Task>
suspend fun getTask(taskId: String): Task?

suspend fun refreshTask(taskId: String)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package com.example.android.architecture.blueprints.todoapp.data.source

import com.example.android.architecture.blueprints.todoapp.data.Result
import com.example.android.architecture.blueprints.todoapp.data.Task
import kotlinx.coroutines.flow.Flow

Expand All @@ -25,15 +24,15 @@ import kotlinx.coroutines.flow.Flow
*/
interface TasksRepository {

fun getTasksStream(): Flow<Result<List<Task>>>
fun getTasksStream(): Flow<List<Task>>

suspend fun getTasks(forceUpdate: Boolean = false): Result<List<Task>>
suspend fun getTasks(forceUpdate: Boolean = false): List<Task>

suspend fun refreshTasks()

fun getTaskStream(taskId: String): Flow<Result<Task>>
fun getTaskStream(taskId: String): Flow<Task?>

suspend fun getTask(taskId: String, forceUpdate: Boolean = false): Result<Task>
suspend fun getTask(taskId: String, forceUpdate: Boolean = false): Task?

suspend fun refreshTask(taskId: String)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,10 @@

package com.example.android.architecture.blueprints.todoapp.data.source.local

import com.example.android.architecture.blueprints.todoapp.data.Result
import com.example.android.architecture.blueprints.todoapp.data.Result.Error
import com.example.android.architecture.blueprints.todoapp.data.Result.Success
import com.example.android.architecture.blueprints.todoapp.data.Task
import com.example.android.architecture.blueprints.todoapp.data.source.TasksDataSource
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext

/**
Expand All @@ -35,17 +30,9 @@ class TasksLocalDataSource internal constructor(
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : TasksDataSource {

override fun getTasksStream(): Flow<Result<List<Task>>> {
return tasksDao.observeTasks().map {
Success(it)
}
}
override fun getTasksStream() = tasksDao.observeTasks()

override fun getTaskStream(taskId: String): Flow<Result<Task>> {
return tasksDao.observeTaskById(taskId).map {
Success(it)
}
}
override fun getTaskStream(taskId: String) = tasksDao.observeTaskById(taskId)

override suspend fun refreshTask(taskId: String) {
// NO-OP
Expand All @@ -55,25 +42,12 @@ class TasksLocalDataSource internal constructor(
// NO-OP
}

override suspend fun getTasks(): Result<List<Task>> = withContext(ioDispatcher) {
return@withContext try {
Success(tasksDao.getTasks())
} catch (e: Exception) {
Error(e)
}
override suspend fun getTasks(): List<Task> = withContext(ioDispatcher) {
return@withContext tasksDao.getTasks()
}

override suspend fun getTask(taskId: String): Result<Task> = withContext(ioDispatcher) {
try {
val task = tasksDao.getTaskById(taskId)
if (task != null) {
return@withContext Success(task)
} else {
return@withContext Error(Exception("Task not found!"))
}
} catch (e: Exception) {
return@withContext Error(e)
}
override suspend fun getTask(taskId: String): Task? = withContext(ioDispatcher) {
return@withContext tasksDao.getTaskById(taskId)
}

override suspend fun saveTask(task: Task) = withContext(ioDispatcher) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@

package com.example.android.architecture.blueprints.todoapp.data.source.remote

import com.example.android.architecture.blueprints.todoapp.data.Result
import com.example.android.architecture.blueprints.todoapp.data.Result.Error
import com.example.android.architecture.blueprints.todoapp.data.Result.Success
import com.example.android.architecture.blueprints.todoapp.data.Task
import com.example.android.architecture.blueprints.todoapp.data.source.TasksDataSource
import kotlinx.coroutines.delay
Expand Down Expand Up @@ -47,41 +44,31 @@ object TasksRemoteDataSource : TasksDataSource {
observableTasks.value = getTasks()
}

override fun getTaskStream(taskId: String): Flow<Result<Task>> {
override fun getTaskStream(taskId: String): Flow<Task?> {
return observableTasks.map { tasks ->
when (tasks) {
is Error -> Error(tasks.exception)
is Success -> {
val task = tasks.data.firstOrNull() { it.id == taskId }
?: return@map Error(Exception("Not found"))
Success(task)
}
}
tasks.firstOrNull { it.id == taskId }
}
}

override suspend fun refreshTask(taskId: String) {
refreshTasks()
}

override fun getTasksStream(): Flow<Result<List<Task>>> {
override fun getTasksStream(): Flow<List<Task>> {
return observableTasks
}

override suspend fun getTasks(): Result<List<Task>> {
override suspend fun getTasks(): List<Task> {
// Simulate network by delaying the execution.
val tasks = TASKS_SERVICE_DATA.values.toList()
delay(SERVICE_LATENCY_IN_MILLIS)
return Success(tasks)
return tasks
}

override suspend fun getTask(taskId: String): Result<Task> {
override suspend fun getTask(taskId: String): Task? {
// Simulate network by delaying the execution.
delay(SERVICE_LATENCY_IN_MILLIS)
TASKS_SERVICE_DATA[taskId]?.let {
return Success(it)
}
return Error(Exception("Task not found"))
return TASKS_SERVICE_DATA[taskId]
}

private fun addTask(title: String, description: String) {
Expand Down
Loading