One Off the Slack: Compose and try/catch

alorma asked an interesting question:

why is not possible to do a try inside a @Composable block?

I had never tried this and would not have expected a problem. And, in truth, it’s not a blanket ban. So, for example, this is fine:

@Composable
fun Content() {
  MyApplicationTheme {

    try {
      throw IllegalStateException("ick!")
    } catch (ex: Exception) {
      Log.d("hello", "world")
    }

    Surface(color = MaterialTheme.colors.background) {
      ShowSomethingCool()
    }
  }
}

However, this is not:

@Composable
fun Content() {
  MyApplicationTheme {
    try {
      Surface(color = MaterialTheme.colors.background) {
        ShowSomethingCool()
      }
    } catch (ex: Exception) {
      Log.d("hello", "world")
    }
  }
}

You cannot call a composable from within a try block of another composable. Calling from a catch block is OK, just not from try.

As various people on the thread pointed out, ideally, your composable is just declaring your UI. Anything with expected exceptions (disk I/O, network I/O, etc.) should be done outside of a composable anyway, such as in a viewmodel.

Google’s Jim Sproch explained that what seems like it might be simple… isn’t:

Supporting try-catch semantics is a lot of engineering work because the way Compose works with recomposition and parallelization, it is possible that a child is called in situations where the parent isn’t on the call stack. But when an exception is thrown, you want the control flow to pass back through the parent, even if the parent was skipped or executed on a different thread. Getting this right requires a decent amount of engineering, and so we had to prioritize other things that were more critical to an initial stable release. Hopefully in a followup release, this can be supported.

So, hopefully, in the future, this will be supported. In the meantime… just don’t write code that crashes, OK?

😁


Read the original thread in the kotlinlang Slack workspace. Not a member? Join that Slack workspace here!