Is there a simple way to get an object of data calss with default value which from string resource file in Kotlin?

Viewed 501

I hope to get an object of data calss with default value which from string resource file.

The code A will not be compiled because I have not to pass a Context paramter for data class MVoice(), I don't think it's a good way.

I there a simple way to get an object of data calss with default value which from string resource file in Kotlin?

Added Content:

If I use Code B, is it a good way ?

Code A

@Entity(tableName = "voice_table", indices = [Index("createdDate")])
data class MVoice(
    @PrimaryKey @ColumnInfo(name = "id") var id: Long = 0,
    var name: String = getString(R.String.Name)
)


<string name="Name">Untitled</string>

Code B

@Entity(tableName = "voice_table", indices = [Index("createdDate")])
data class MVoice(
    @PrimaryKey @ColumnInfo(name = "id") var id: Long = 0,
    var name: String
)
{
    companion object {
        fun getDefaultMVoice(mContext: Context): MVoice {
            return MVoice(name = mContext.getString(R.string.name))
        }
    }
}
5 Answers

I assume you want the default value of the name variable to be displayed somewhere in your app's UI. I would create an extension function on MVoice object:

fun MVoice.nameOrDefault(ctx: Context) =
    if (name == null || name.trim().isEmpty()) {
        MVoice.defaultName(ctx)
    } else {
        name
    }

data class MVoice(...) {

    // ...

    companion object {
        @JvmStatic
        fun defaultName(ctx: Context) = ctx.getString(R.string.name)
    }
}

We can use that function to set text, for example, to TextView:

val voice: MVoice = ...
val textView: TextView = ...
textView.text = voice.nameOrDefault(context)

You should not add logic or Android specific code to your data classes which makes unit testing impossible.

You should instead use a business logic layer(usecase/inteactor) or ViewModel that has interface and that interface should have concrete implementation that takes context that returns a default string. It's like injecting context to db if you are familiar with dagger.

You can also use AndroidViewModel class either for accessing context.

You cannot get string resources without a context, however you can use the application context and make it static. You have to declare in manifest:

<application android:name="com.xyz.App">

</application>

And then make the application context static:

class App: Application() {
    companion object {
        lateinit var appContext: WeakReference<Context>
    }

    override fun onCreate() {
        super.onCreate()
        appContext = WeakReference(applicationContext)
    }
}

Then you can use the context everywhere you need it by calling: App.appContext.get()!!.getString(R.string.example)

Yes, we can access resources without using Context To get a value from string resource file for data class in Kotlin:

Resources.getSystem().getString(R.string.app_name)

you can use them everywhere in your application, even in static constants declarations!

eg:

@Entity(tableName = "Support")
data class Support(
        @PrimaryKey @ColumnInfo(name = "support_id") val support_id: String,
        @ColumnInfo(name = "support_count")       val support_count: Int,
        @ColumnInfo(name = "support_visit")       val support_visit: String= Resources.getSystem().getString(R.string.app_name)
)

I just gave a solution for your answer, but I do recommended you to use direct string value.

I think what you're trying to do is not a good idea on it's own, so even if you can, the question is if you should. It basically means that your data class needs to know about Android resources and UI (R.string, Context), which is questionable design

I'd go with

@Entity(tableName = "voice_table", indices = [Index("createdDate")])
data class MVoice(
    @PrimaryKey @ColumnInfo(name = "id") var id: Long = 0,
    var name: String
)

as it doesn't have the default value you always have to provide it, then you just call

MVoice(requireContext().getString(R.string.mvoicedefault))
Related