-
Notifications
You must be signed in to change notification settings - Fork 13
R8 ProGuard
retrofit-graphql is R8-safe when using the recommended kotlinx.serialization path. This page documents the R8 configuration from the sample app and guidance for different serialization configurations.
// app/build.gradle.kts
android {
buildTypes {
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
}
}
testBuildType = "release" // compile unit tests against the release variant
}The kotlinx.serialization consumer rules are shipped with the kotlinx-serialization-core/-json artifacts themselves and protect every @Serializable class on the consuming classpath. No custom keep rules are required for:
- Generated response DTOs (
GetCurrentUserData,GetMarketPlaceAppsData, etc.) - The legacy
@Serializablemodels in:compat(GraphContainer<T>,GraphErrorandGraphError.Location,GraphQLRequest<TVariables>,PersistedQuery) - Any enum, variable, or input object class generated by the codegen plugin
EmptyGraphQLVariables is a plain object (not @Serializable) and needs no rules. The neutral GraphQLOperationRequest/GraphQLResponse contracts are serialized structurally by the explicit codec without reflection, so R8 is free to rename them -- the sample's verifyReleaseMapping asserts exactly that.
No library module ships keep rules. The historical
:libraryconsumer rule (-keep io.github.wax911.library.model.**) was removed because theio.github.wax911.library.*aliases emit only empty*Ktfile-facade classes. R8 rules are module-owned (kotlinx artifact rules, Retrofit/OkHttp artifact rules) or consumer-owned (your Gson rules below).
The sample app uses Gson for the multipart upload path (UploadMutationHelper). Gson derives JSON keys from java.lang.reflect.Field.getName(), so field names must survive R8 renaming:
# app/proguard-rules.pro
# GraphQLRequest: serialized by Gson in UploadMutationHelper.createOperationsPart()
-keep class co.anitrend.retrofit.graphql.model.GraphQLRequest {
<fields>;
<init>(...);
}
# UploadToStorageBucketVariables: serialized by Gson as nested variables
# within the operations payload of a multipart upload request.
-keep class co.anitrend.retrofit.graphql.sample.bucket.UploadToStorageBucketVariables {
<fields>;
<init>(...);
}
These rules are narrowly scoped to the exact classes used in the Gson upload path. The -keep directive preserves the class and all its members (fields, methods) so Gson's reflective serialization works correctly.
If you use withPersistedQuery() in the Gson upload path, PersistedQuery is serialized as part of the request. Add:
-keep class co.anitrend.retrofit.graphql.model.request.PersistedQuery {
<fields>;
}
This is only needed if your Gson path involves APQ. On the neutral path, GraphQLOperationRequest.withPersistedQuery() stores the extension structurally as GraphQLValue.ObjectValue, which the codec serializes without reflection -- no keep rule needed. The legacy typed kotlinx request path supports withPersistedQuery() because KotlinxGraphQLJson.encode() (:compat) merges supported extensions values into the outgoing JSON.
./gradlew :app:testReleaseUnitTestThis runs JVM unit tests against release-variant classes. These tests do not execute minified R8 bytecode. The sample app has 11 unit tests (6 serialization + 5 mapper) that validate:
-
GetCurrentUserDatacan be deserialized from JSON -
GetMarketPlaceAppsDatacan be deserialized from JSON -
@SerialNameannotations are present on release-variant generated classes - Error responses deserialize on the release JVM classpath
- Empty data responses deserialize on the release JVM classpath
./gradlew :app:releaseR8Verification -Pandroid.testoptions.manageddevices.emulator.gpu=swiftshader_indirectThis is the sample app's R8 runtime gate. It runs verifyReleaseMapping plus the managed-device pixel2api30ReleaseAndroidTest task. ReleaseSerializationTest validates serialization behavior on an actual emulator running the R8-optimized APK.
# Check what R8 renamed
cat app/build/outputs/mapping/release/mapping.txtVerify that no serialization-critical classes or fields are renamed:
- Generated response DTO classes may be renamed, but their kotlinx serializers and wire-name literals must remain usable
-
GraphQLRequestfields (query,operationName,variables) should not be renamed -- this class is kept for the Gson upload path (legacy,:compat) -
UploadToStorageBucketVariablesfield (upload) should not be renamed -
GraphQLOperationRequestshould be renamed -- the explicit codec does not use reflection, and the sample'sverifyReleaseMappingasserts it is not accidentally kept
The sample release verification covers the kotlinx generated response runtime and the Gson multipart upload path. Concrete Gson response DTO support is covered by codegen functional tests; applications that deserialize generated response DTOs through Gson in release should add scoped keep rules and their own mapping or device verification.
You do not need keep rules for:
- Generated response DTOs (
*Dataclasses) -- protected by the kotlinx artifact consumer rules when they are@Serializable - Legacy
@Serializablemodels in:compat(GraphContainer,GraphError,GraphQLRequest,PersistedQuery) -- protected by the same kotlinx artifact rules -
EmptyGraphQLVariables-- plain object, no serializer state -
GraphQLOperationRequest/GraphQLResponse/GraphQLValue-- serialized structurally by the explicit codec, no reflection - Retrofit interfaces -- Retrofit ships its own consumer rules
- OkHttp/Okio types -- they ship their own consumer rules
If your project uses Gson for the entire Retrofit path (no kotlinx), you need consumer-owned keep rules for every class serialized/deserialized by Gson. The library does not ship Gson keep rules for consumer model classes; the sample app keeps only the two classes of its own upload path:
# Your Gson-serialized model classes
-keep class com.your.app.model.** {
<fields>;
}
If you deserialize generated @SerializedName response DTOs through Gson in release, add scoped rules for those DTOs and verify with your own mapping or device check (the sample release gate does not exercise Gson response DTO decoding).
- Serialization Backends -- kotlinx and Gson backend details and R8 safety comparison