티스토리 뷰

We have use cases when we have to pass String message from ViewModel to View often. In Android,  the system support String resources and it would be nice to use String resource instead of hard coded strings whenever possible. You might have had some situations you need to pass String resources from your ViewModel to activity or fragment but you don't want to pass Context to your ViewModel. You might have wrapper of String resources in use this in your ViewModel.

 

interface StringResource {
    fun getString(resId: Int): CharSequence
}

class StringResourceImpl(private val context: Context) {
   override fun getString(resId: Int): CharSequence {
      return context.getString(resId)
   }
}

class MyViewModel(
   private val stringResource: StringResource
) {
   ...
}

StringResourceImpl class still has Context reference of your View and extracting specific strings from resources is not ViewModel's job. It would be enough to have resource id in ViewModel.

 

I have slightly different option for the case. We don't hold Context reference when we instantiate the wrapper but we can use context when we actually get a string using context parameter.  Here is my implementation:

 

sealed class Txt {
   abstract fun getString(context: Context): CharSequence()
   class R(val resId: Int, vararg args: Txt): Txt() {
      private val fmtArgs = args

      override fun getString(context: Context): CharSequence {
        if (fmtArgs.isNotEmpty()) {
            val strArgs = fmtArgs.map { txt ->
                txt.getString(context)
            }.toTypedArray()
            return context.getString(resId, *strArgs)
        }

        return context.getString(resId)
      }
   }
   
   class S(private val s: CharSequence) : Txt {
     override fun getString(context: Context): CharSequence {
        return s
     }
   }
}

 

StringResources has two different types, CharSequence and Resource ID.  We can wrap it using sealed class and we will use Context only when we call getString function from a View. Also, we can pass Txt instance into Txt.R class and extract strings by calling Context.getString(resId, fmtArgs).

 

We could use Txt class like below.

 

strings.xml:

<resources>
   <string name="error_guide_message">Something went wrong. Please contact call %1$s for further help.</string>
   <string name="call_center_phone_no">123–4567</string>
</resources>

 

object CommonStr {
    val callCenterPhoneNo by lazy { Txt.R(R.string.call_center_phone_no) }
}

val errorGuideMessage = Txt.R(R.string.error_guide_message, CommonStr.callCenterPhoneNo)
errorGuideMessage.getString(requireContext())

output:
Something went wrong. Please contact call 123–4567 for further help.

 

We could take the code bit further using inline class to improve performance. I am not sure how much I would gain but I believe there would be some performance benefit because most of application use internalisation strings a lot.

Here is inline class version.

sealed interface Txt : Serializable {
    fun getString(context: Context): CharSequence
}

@JvmInline
value class Str(private val s: CharSequence) : Txt {
    override fun getString(context: Context): CharSequence {
        return s
    }
}

@JvmInline
value class StrR(private val resId: Int) : Txt {
    override fun getString(context: Context): CharSequence {
        return context.getString(resId)
    }
}

class StrRArgs(private val resId: Int, private val args: List<Txt>) : Txt {
    override fun getString(context: Context): CharSequence {
        if (args.isNotEmpty()) {
            val strArgs = args.map { txt ->
                txt.getString(context)
            }.toTypedArray()
            return context.getString(resId, *strArgs)
        }

        return context.getString(resId)
    }
}

I splited Res and ResArgs class and ResArgs cannot become inline class because it has two constructor parameters. But Str and Res are inline classes and ResArgs also will have some benefit from this change.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함