Search code examples
androidkotlindependency-injectionmoshikotlin-extension

Using Moshi in an extension function or injecting it via DI in Android


In Android, I am using an extension function to map a failed network response to my sealed ErrorResponse class.

@JsonClass(generateAdapter = true)
data class ErrorResponse(
    @Json(name = "code") val code: String,
    @Json(name = "message") val message: String,
    @Json(name = "id") val id: String,
    @Json(name = "path") val path: String
)

The "problem" is that I need to use Moshi JsonAdapter to parse the JSON Response to this ErrorResponse class.

I have the following lines:

val moshi = Moshi.Builder().build()
val adapter: JsonAdapter<ErrorResponse> = moshi.adapter(ErrorResponse::class.java)
val error = try {
    adapter.fromJson(it.response()?.errorBody()?.string() ?: "")
} catch (e: JsonDataException) {
    Timber.e(e, "Network response: JsonDataException")
    ErrorResponse.empty()
}

But as I said we are using an extension fun to map the response so I don't think it's optimal to create a new Moshi object on every API request (?).

I'm also using Koin and I can inject Moshi if needed.

My question is, which is the most optimal way of achieving this in order to not create multiple Moshi objects (and therefore have an impact on Performance/Memory)?

  1. Create a Moshi object as a top level variable
  2. Use an object in Kotlin which will inject Moshi and define this fun, whereas Moshi is declared as single in Koin so it's essentially a singleton?
  3. Inject Moshi the extension function?
  4. Other??

Solution

  • We use Module to keep Moshi object.

    factory<Moshi> {
        Moshi.Builder()
            ...
            .build()
    }
    
    factory<JsonAdapter<ErrorResponse>> {
        get<Moshi>().adapter(ErrorResponse::class.java)
    }
    

    In another Module we inject it:

    factory {
        SomeInteractor(
            // moshi = get(),
            moshiAdapter = get(),
        )
    }
    

    And then call it in interactors like:

    class SomeInteractor(
        // private val moshi: Moshi,
        private val moshiAdapter: JsonAdapter<ErrorResponse>,
    ) {
    }