moshi: Moshi-kotlin does not work with proguard

When ProGuard is enabled the call rawType.kotlin.primaryConstructor in KotlinJsonAdapterFactory returns null so it exits early.

I created a sample repository here which demonstrates the issue: https://github.com/grandstaish/moshi-kotlin-reflection

The only way I can get Moshi to work currently is to uncomment this line in proguard (-keep class kotlin.** { *; }). However, keeping literally everything in kotlin is going way overboard! Is there a better solution to this?

I have tried to figure this out for too long now and I’m going to have to pass this issue onto the experts, sorry! 😞

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 8
  • Comments: 15 (7 by maintainers)

Commits related to this issue

Most upvoted comments

Update to my previous comments: This issue doesn’t seem to be related to the enum, since it persisted after I got back to raw Strings. Only keeping the models solved the issue. Here are my proguard rules:

#### OkHttp, Retrofit and Moshi
-dontwarn okhttp3.**
-dontwarn retrofit2.Platform$Java8
-dontwarn okio.**
-dontwarn javax.annotation.**
-keepclasseswithmembers class * {
    @retrofit2.http.* <methods>;
}
-keepclasseswithmembers class * {
    @com.squareup.moshi.* <methods>;
}
-keep @com.squareup.moshi.JsonQualifier interface *
-dontwarn org.jetbrains.annotations.**
-keep class kotlin.Metadata { *; }
-keepclassmembers class kotlin.Metadata {
    public <methods>;
}

-keepclassmembers class * {
    @com.squareup.moshi.FromJson <methods>;
    @com.squareup.moshi.ToJson <methods>;
}

-keepnames @kotlin.Metadata class com.myapp.packagename.model.**
-keep class com.myapp.packagnename.model.** { *; }
-keepclassmembers class com.myapp.packagename.model.** { *; }

These may be overly conservative, but at least, my app works properly with these. I’m waiting for better proguard rules, if any are possible. Hope it helps anyone encountering the same issue!

@JakeWharton was right all along 😥:

The required rules for kotlin are:

-dontwarn org.jetbrains.annotations.**
-keep class kotlin.Metadata { *; }

Also it might be worth mentioning that you need to keep the constructors and fields of your model classes too:

-keepclassmembers class my.models.package.** {
  <init>(...);
  <fields>;
}

Do you want me to create a PR to update the README.md?

Since my last comment, I have had to add a rule to keep all of my model class names too. This is because the fully qualified classname is kept as a String in the @Metadata annotation, and sometimes kotlin-reflect will use it. For anyone interested, the rule looked like so:

-keepnames @kotlin.Metadata class my.models.package.**

I also changed another rule in the README to be more specific:

-keepclassmembers class * {
    @com.squareup.moshi.FromJson <methods>;
    @com.squareup.moshi.ToJson <methods>;
}

So far this has been working, but I’ll comment in here if we find anything else.

Edit: @ktchernov you could also define your own annotation, and then define specific proguard rules to apply for classes with that annotation. That way you’ll still get some small amount of obfuscation + you’ll strip all the methods that aren’t being used.

Update for my last comment: I still use Moshi, but replaced moshi-kotlin with kotshi, which plays fine with Proguard without special rules, dropping hundreds of kilobytes in the final apk.

Keeping Kotlin meta data is not enough. And keeping all constructors and fields of model classes is really annoying to do. Unless you move all your model classes into one package.

I’m trialling a workaround to add the Android Support annotation @Keep on model classes. @Keep has a rule of -keep @android.support.annotation.Keep class * {*;} in the defaultproguard-android.txt. May be enough to do the trick.

Are there any workarounds for this? Using moshi-kotlin and ProGuard is a pretty common occurrence… I keep getting a dreaded IllegalArgumentException: Unable to create converter for class (...) exception when ProGuard is enabled.

There are two factors for proguard rules.

1 - rules for kotlin-reflect. These have been historically finicky, but kotlin 1.4 will finally ship with minimal rules embedded directly in the kotlin-reflect artifact. These ensure that kotlin-reflect’s machinery works, and should be the same for all users (hence them being bundled in the kotlin-reflect jar now). But this alone is not enough!

2 - rules for reflective serialization. This is the same problem that has always existed with reflective serialization and you have to keep any classes or fields/properties that you want to reflectively serialize. There are unique per application because they’re your models. Common patterns for this include keeping a common models package with wildcards or denoting them with a marker annotation and keeping anything annotated with it.

For those that have consistent reproduction cases, try with Kotlin 1.4 (specifically with kotlin-reflect 1.4 or copy the rules from here) and ensure your proguard rules separately have rules for your reflectively serialized models.

If you still have issues with both, report back with a minimally reproducible sample project link. My hope though is that this is resolved with the combination of first-party rules and ensuring your own project’s rules are correct.

@marcosalis Just above your message, there’s one, yes. Now, you can also use moshi kotlin codegen, see the README.

Keep Kotlin’s metadata annotation. That’s all that should be needed.

On Mon, Aug 28, 2017 at 1:02 PM Jesse Wilson notifications@github.com wrote:

Oooh, yes that’s tough. Not sure exactly what we’ll need to do.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/square/moshi/issues/345#issuecomment-325412488, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEEEfYk35cpB3LDcQEGtideuZEt4VLyks5scvKlgaJpZM4PDpvQ .