From 02175b193bd1b6f46ebbae60bfd93d9c310b1b0d Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Fri, 22 May 2026 21:05:53 -0400 Subject: [PATCH] =?UTF-8?q?fix(android):=20drop=20provideLibraryApi=20@Pro?= =?UTF-8?q?vides=20=E2=80=94=20repos=20construct=20from=20Retrofit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "ModuleProcessingStep was unable to process NetworkModule because LibraryApi could not be resolved" failure under KSP2 + Hilt 2.59.2 turns out to be specific to @Provides returning a hand-written Kotlin interface that carries no KSP-processed annotations. Hilt's ModuleProcessingStep resolves the return type through KSP2's API and gets an ERROR type for source-only interfaces in some configurations (google/dagger#4303 cluster). Two source-of-truth interfaces I tested side-by-side: - AuthSessionDao (@Dao, Room-processed) — @Provides works - LibraryApi (only @GET Retrofit annotations, no KSP processor) — fails Workaround that's actually a better pattern: feature repositories construct their Retrofit interface from the Hilt-injected shared Retrofit instance. Fewer bindings in the Hilt graph; one Retrofit interface lives next to its sole consumer. LibraryApi.kt + wire types remain; LibraryRepository (Phase 5.2) will hold the `retrofit.create()` call. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../fabledsword/minstrel/api/NetworkModule.kt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/android/app/src/main/java/com/fabledsword/minstrel/api/NetworkModule.kt b/android/app/src/main/java/com/fabledsword/minstrel/api/NetworkModule.kt index 9a584b6f..b937b478 100644 --- a/android/app/src/main/java/com/fabledsword/minstrel/api/NetworkModule.kt +++ b/android/app/src/main/java/com/fabledsword/minstrel/api/NetworkModule.kt @@ -1,7 +1,6 @@ package com.fabledsword.minstrel.api import com.fabledsword.minstrel.BuildConfig -import com.fabledsword.minstrel.api.endpoints.LibraryApi import com.fabledsword.minstrel.auth.AuthStore import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import dagger.Module @@ -13,7 +12,6 @@ import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit -import retrofit2.create import java.util.concurrent.TimeUnit import javax.inject.Singleton @@ -72,9 +70,19 @@ object NetworkModule { .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) .build() - @Provides - @Singleton - fun provideLibraryApi(retrofit: Retrofit): LibraryApi = retrofit.create() + // Per-endpoint Retrofit interface @Provides intentionally NOT here. + // Feature repositories construct their interface via the shared + // Retrofit (Hilt-injected), e.g.: + // + // class LibraryRepository @Inject constructor(retrofit: Retrofit) { + // private val api = retrofit.create() + // ... + // } + // + // Fewer Hilt bindings + no KSP2 type-resolution sharp edges from + // Hilt processing a `@Provides` return type that's a hand-written + // Kotlin interface (Hilt's ModuleProcessingStep "could not be + // resolved" failure mode under KSP2 — google/dagger#4303). private const val CONNECT_TIMEOUT_SECONDS = 10L private const val READ_TIMEOUT_SECONDS = 30L