Hi guys, I want to know how to avoid `null` checks...
# compose
k
Hi guys, I want to know how to avoid
null
checks before setting into the UI component. Is there any better way to avoid
null
checks in composable function? For example data is coming from the server and it may be
null
. I am giving an example
Copy code
data class TestItem(
    val link: String? = null,
    val result: Result? = null
)
More code in a thread.
Copy code
data class Result(
    val name: String? = null,
    val iconUrl: String? = null,
)
When we pass
TestItem
list to composable function we have to check that it's not null
Copy code
testItem.result?.name?.let {
    Text(
        text = it,
        maxLines = 1,
    )
}
Can we avoid this null check?
c
A good idea, architecturally-speaking, is to use the Repository pattern to make the API call and clean up the response. The Repository then exposes to the UI an object containing data from the API, but cleaned up and made easier to use from the UI. This Repository layer also serves to protect you against changes in the API. If the API response changes, you only need to update the Repository, instead of every spot in the UI using the affect API value. This page in the Android docs talks more this pattern
k
Thanks Casey, for the direction. I'll read the doc now. I have few doubts regarding your comment. 1.
Repository pattern to make the API call and clean up the response
I got the point to make
API call
but not
clean up
process. If I take above example,
TestItem
and
Result
. I have to make a duplicate class and it doesn't have null values? Something like this below
Copy code
data class TestItem(
    val link: String,
    val result: Result,
)
Copy code
data class Result(
    val name: String,
    val iconUrl: String,
)
Pass this object to composable function? Is I am thinking right?
c
For example, you would have one data class which deserialized the JSON API response, and another which is used by the UI. The Repository then copies the data from the API response into the UI model, and in the process can do things like convert nulls to default values, convert obscure server error codes into readable messages, and other logic like that. As a very simplified example, this is the general idea. In other words, the Repository class makes sure that no null values make it to the UI (or whatever other logic is needed), so you don’t need special handling for null values in Compose
Copy code
@Serializable
data class TestItemResponse(
    val link: String? = null,
    val result: String? = null, // could be 00 for success, 01 for an error
)

enum class TestItemResult { 
    Success, Error, Unknown
}

data class TestItem(
    val link: String,
    val result: TestItemResult,
)

class Api {
    fun getTestItem(): TestItemResponse {
        TODO() 
    }
}

class Repository(val api: Api) { 
    fun getTestItem(): TestItem { 
        val apiResponse = api.getTestItem()
        
        return TestItem(
            link= apiResponse.link ?: "",
            result = when(apiResponse.result) { 
                "00" -> TestItemResult.Success
                "01" -> TestItemResult.Error
                else -> TestItemResult.Unknown
            }
        )
    }
}
It may initially seem like a lot of extra work to have 2 data classes for 1 API, but it actually saves you a ton of work overall as you don’t have to make the same null checks over-and-over in the UI, and makes it easier to find and correct bugs since the additional things like null checks or value conversions are kept in one centralized place.
k
I really appreciate it. I got your point ✌️
a
If you need to show some text conditionally you always need an if clause. It's just null check vs
isNotEmpty()
check.