1

I have a function like this that inserts a new row in a table with rowId:

@Composable
fun AddNewCustomer() {
    val db = CustomersDatabase.getDatabase(LocalContext.current)
    val coroutineScope = rememberCoroutineScope()
    val createMainEntity = {
        coroutineScope.launch(Dispatchers.IO) {
            val rowId = db.customersDao().getLast()!!.id + 1
            db.customersDao().insertPrimaries(CustomersEntity(rowId, null, null, null))

        }
    }
    createMainEntity()
    otherFunc(db, coroutineScope)      
    
}

and in another function, I insert a new row to another table:

@Composable
private fun otherFunc(
    db: CustomersDatabase,
    coroutineScope: CoroutineScope,
) {
        val save = {
                coroutineScope.launch(Dispatchers.IO) {
                    delay(100)
                    val rowId = db.customersDao().getLast()!!.id
                    db.customersDao().insertPhone(PhoneNumberEntity(phone = "", customerId = rowId , field = "" ))
   }
   }
        save()
}

I want to save() in otherFunc waits till createMainEntity finishes, with delay, I can be sure the createMainEntity finishes first but it's a dirty way how can I do that better.

رضا پریور
  • 556
  • 1
  • 6
  • 16
  • You need two suspend functions, one for adding user and otherFunc. And then call them one after another inside launch block of your scope. See: https://kotlinlang.org/docs/composing-suspending-functions.html#sequential-by-default – bylazy Jan 27 '22 at 10:50
  • Does `AddNewCustomer` or `otherFunct` need to be `@Composable`? They don't look like they create UI to me. Where are you calling each of them from? I barely started reading about Compose, but isn't it critical that Composable functions not create side effects? Otherwise, when the screen gets recomposed, you'll be performing side effects again at an unexpected time. – Tenfour04 Jan 27 '22 at 14:33
  • @Tenfour04 yes i skipped UI codes – رضا پریور Jan 27 '22 at 15:37
  • So where do you call them from? – Tenfour04 Jan 27 '22 at 15:53

1 Answers1

0

Based on what I understand about Compose, these functions absolutely should not be Composables. A Composable function must not affect external state because it must be idempotent. You could call one of these functions from a listener lambda in one of your Composables, but you must not call them directly.

Accordingly, the function name should not start with a capital letter by convention. I also added "Async" to the name to signify that it does something asynchronously (by launching a coroutine that runs separately).

To call the other function after the Room transaction is complete, call it inside the coroutine that is performing the transaction.

Also, it is convoluted to create a lambda function solely to immediately call it and do nothing else with it, so I removed that. Your fixed function looks like:

fun addNewCustomerAsync(coroutineScope: CoroutineScope) {
    val db = CustomersDatabase.getDatabase(LocalContext.current)
    coroutineScope.launch(Dispatchers.IO) {
        val rowId = db.customersDao().getLast()!!.id + 1
        db.customersDao().insertPrimaries(CustomersEntity(rowId, null, null, null))
        createMainEntity() 
        otherFunc(db, coroutineScope) 
    }
}

Your other function should simply be a suspend function. A dispatcher doesn't need to be specified because it doesn't call any blocking functions.

private suspend fun otherFunc(db: CustomersDatabase) {
    delay(100) // Why are you doing this? Probably can remove this line.
    val rowId = db.customersDao().getLast()!!.id
    db.customersDao().insertPhone(PhoneNumberEntity(phone = "", customerId = rowId , field = "" ))
}

You can call addNewCustomerAsync() from a listener/callback in one of your Composable functions, but you cannot call it directly. Really, these kinds of function that interact with a database should be in a ViewModel, so I would remove the coroutineScope parameter from addNewCustomerAsync() and have it use viewModelScope.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • Also, instead of manually creating IDs, I would use `autogenerate`. See here: https://stackoverflow.com/questions/44109700/how-to-make-primary-key-as-autoincrement-for-room-persistence-lib – Tenfour04 Jan 27 '22 at 16:08
  • Composable functions with the Unit return must start with upper case also, I need to create ids manually also otherFunc is a composable function so it can't run from a coroutine – رضا پریور Jan 27 '22 at 16:50
  • It is forbidden for otherFunc to be Composable because it’s writing to a repo. Likewise since addNewCustomer calls otherFunc, it cannot be Composable either. You need to think reactively instead of imperatively when using Compose. – Tenfour04 Jan 27 '22 at 16:54