Get provides from another scope

22 views Asked by At

There is an Android application Jetpack Compose. Dagger2 is used for DI (v2.45).

1. The following scopes are available:


    @Scope
    annotation class AppScope
    
    @Scope
    annotation class MainScreenScope
    
    @Scope
    annotation class SettingsScreenScope
    
    @Scope
    @Retention(AnnotationRetention.RUNTIME)
    annotation class P7LibScope

2. The Persistance package has DI modules with classes for Provides and UseCases for database manipulation:


    @Module
    class MappersModule {
    
        @[Provides MainScreenScope]
        fun providesBaseSettingsMapper(): Mapper<BaseSettingsDTO, BaseSettingsDB> = BaseSettingsMapper()
    
        /* other Mappers */
    }


    @Module
    class PersistenceModule {
    
        @[Provides MainScreenScope]
        fun providesStoreStrategy(): StoreStrategy = DatabaseStoreStrategy()
    
        @[Provides MainScreenScope]
        fun provideBaseSettingsPersistence(
            baseSettingsDao: BaseSettingsDao,
            mapper: Mapper<BaseSettingsDTO, BaseSettingsDB>,
            storeStrategy: StoreStrategy,
        ): BaseSettingsPersistence = BaseSettingsPersistenceImpl(baseSettingsDao, mapper, storeStrategy)
    
        /* other Persistance */
    }


    interface DatabaseUseCase {
        suspend fun getShiftParams(): ShiftParamsDTO
        suspend fun getBaseSettings(): BaseSettingsDTO
        suspend fun getReceiptNumber(): Long
        suspend fun addTransaction(transactionDTO: TransactionDTO)
    }


    class UseCaseExampleImpl @Inject constructor(
        private val baseSettingsPersistence: SettingsPersistence,
        private val receiptPersistence: ReceiptPersistence,
        private val transactionsPersistence: TransactionsPersistence,
    ) : DatabaseUseCase {
        override suspend fun getShiftParams(): ShiftParamsDTO {
            return baseSettingsPersistence.getShiftSettings()
        }
    
        override suspend fun getBaseSettings(): BaseSettingsDTO {
            return baseSettingsPersistence.getBaseSettings()
        }
    
        override suspend fun getReceiptNumber(): Long {
            return receiptPersistence.getCurrentReceiptNumber()
        }
    
        override suspend fun addTransaction(transaction: TransactionDTO) {
            transactionsPersistence.add(transaction)
            receiptPersistence.incrementReceiptNumber()
        }
    }

3. В Room-пакете имеется DI-модуль для Provides


    @Module
    class RoomModule {
        @[Provides MainScreenScope]
        fun providesAppDatabase(context: Context): AppDatabase = AppDatabase.getInstance(context)
    
        @[Provides MainScreenScope]
        fun provideBaseSettingsDao(appDatabase: AppDatabase): BaseSettingsDao =
            appDatabase.baseSettingsDao()
    
        /* other Dao */
    }

4. In app-package AppComponent, AppComponentDependencies, AppModule, MainScreenComponent, MainScreenComponentDependencies, MainScreenModule, SubcomponentModule:


    /**
     * Main app component
     */
    @Component(
        modules = [
            AppModule::class,
            SubcomponentModule::class,
            NetworkModule::class,
            EvatorPrinterModule::class,
            EvotorSDKModule::class,
        ],
        dependencies = [AppComponentDependencies::class],
    )


    @AppScope
    interface AppComponent : MainScreenComponentDependencies, P7LibComponentDependencies, NetworkComponentDependencies {
        @Component.Builder
        interface Builder {
    
            @BindsInstance
            fun application(application: Application): Builder
    
            fun appComponentDependencies(dependencies: AppComponentDependencies): Builder
    
            fun build(): AppComponent
        }
    
        fun inject(application: App)
    
        fun mainScreenComponentBuilder(): MainScreenComponent.Builder
    }


    interface AppComponentDependencies {
        val context: Context
    }
    ```
    
    ```
    @Module
    object AppModule {
        @[Provides AppScope]
        fun providesP7LibRepository(): IP7LibRepository = P7LibRepositoryImpl()
    
        @[Provides AppScope]
        fun provideUseCase(useCaseExampleImpl: UseCaseExampleImpl): DatabaseUseCase {
            return useCaseExampleImpl
        }
    
        @[Provides AppScope]
        fun providesP7LibCallbacks(useCaseExample: DatabaseUseCase, gatewayServerRepository: GatewayServerRepositoryApi): IP7LibCallbacks =
            P7LibCallbacksImpl(useCaseExample, gatewayServerRepository)
    
        @[Provides AppScope]
        fun providesLogger(context: Context): ErrorLogger = FileLogger(context)
    }


    @MainScreenScope
    @Subcomponent(
        modules = [
            RoomModule::class,
            PersistenceModule::class,
            MappersModule::class,
            MainScreenModule::class,
        ],
    )


    interface MainScreenComponent {
        /**
         * Инициализация полей MainActivity
         */
        fun inject(mainActivity: MainActivity)
    
        /**
         * Builder компоненты основного экрана
         */
        @Subcomponent.Builder
        interface Builder {
            fun mainModule(module: MainScreenModule): Builder
            fun roomModule(module: RoomModule): Builder
            fun mappersModule(module: MappersModule): Builder
            fun persistenceModule(module: PersistenceModule): Builder
    
            fun build(): MainScreenComponent
        }
    }


    /**
     * Зависимости компоненты
     */
    interface MainScreenComponentDependencies
    
    @Module
    class MainScreenModule {
        @[Provides MainScreenScope]
        fun provideConfigurationFileReader(context: Context): ConfigurationFileReader {
            return ConfigurationFileReader(context)
        }
    
        @[Provides MainScreenScope]
        fun providesCardReaderRepository(sdkRepository: ISDKRepository): CardReaderRepository =
            object : CardReaderRepository {
                override val sdkRepository: ISDKRepository
                    get() = sdkRepository
            }
    
        @[Provides MainScreenScope]
        fun providesMainActivityViewModel(
            repository: IP7LibRepository,
            configurationFileReader: ConfigurationFileReader,
            settingsPersistence: SettingsPersistence,
            cardReader: CardReaderRepository,
            callBacks: IP7LibCallbacks,
        ): MainActivityViewModel = MainActivityViewModel(
            p7LibraryRepository = repository,
            configurationFileReader = configurationFileReader,
            settingsPersistence = settingsPersistence,
            cardReaderRepository = cardReader,
            p7LibCallbacks = callBacks,
        )
    
        @[Provides MainScreenScope]
        fun provideNavHostController(
            debitViewModelFactory: DebitViewModel.DebitViewModelFactory,
            settingsViewModelFactory: SettingsViewModel.SettingsViewModelFactory,
        ): PosNavController {
            return PosNavController(debitViewModelFactory, settingsViewModelFactory)
        }
    }


    @Module(subcomponents = [MainScreenComponent::class])
    object SubcomponentModule{}

5. App.kt:


    class App : Application() {
    
        private val applicationScope = CoroutineScope(SupervisorJob())
    
        companion object {
            /**
             * Основная компонента приложения
             */
            lateinit var appComponent: AppComponent
        }
    
        override fun onCreate() {
            super.onCreate()
    
            App.appComponent = DaggerAppComponent
                .builder()
                .application(this)
                .appComponentDependencies(AppComponentDependenciesImpl())
                .build()
    
            appComponent.inject(this)
        }
    
        private inner class AppComponentDependenciesImpl : AppComponentDependencies {
            override val context: Context = this@App
        }
    }
    
    val Context.appComponent: AppComponent
        get() = App.appComponent

6. MainActivity.kt:


    class MainActivity : ComponentActivity() {
    
        /**
         * ViewModule основного экрана
         */
        @Inject
        lateinit var viewModel: MainActivityViewModel
    
        @Inject
        lateinit var debitViewModelAssistedFactory: DebitViewModel.DebitViewModelFactory
    
        @Inject
        lateinit var settingsViewModelAssistedFactory: SettingsViewModel.SettingsViewModelFactory
    
        @Inject
        lateinit var posNavController: PosNavController
    
        lateinit var mainScreenSubcomponent: MainScreenComponent
    
        @SuppressLint("UnusedMaterialScaffoldPaddingParameter")
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            mainScreenSubcomponent = App.appComponent.mainScreenComponentBuilder()
                .mainModule(MainScreenModule())
                .roomModule(RoomModule())
                .mappersModule(MappersModule())
                .persistenceModule(PersistenceModule())
                .build()
    
            mainScreenSubcomponent.inject(this)
    
            setContent {
                PetrolPlusTheme {
                    Surface {
                        // ui
                    }
                }
            }
        }
    }


PROBLEM:

    C:\AppComponent.java:11: error: [Dagger/MissingBinding] ru.elka.prj.persitence.SettingsPersistence cannot be provided without an @Provides-annotated method.
    public abstract interface AppComponent extends ru.elka.prj.di.MainScreenComponentDependencies, ru.elka.prj.p7Lib.di.P7LibComponentDependencies, ru.elka.prj.network.di.NetworkComponentDependencies {
                    ^
      A binding for ru.elka.prj.persitence.SettingsPersistence exists in ru.elka.prj.di.MainScreenComponent:
          ru.elka.prj.persitence.SettingsPersistence is injected at
              [ru.elka.prj.di.AppComponent] ru.elka.prj.persitence.UseCaseExampleImpl(baseSettingsPersistence)
          ru.elka.prj.persitence.UseCaseExampleImpl is injected at
              [ru.elka.prj.di.AppComponent] ru.elka.prj.di.AppModule.provideUseCase(useCaseExampleImpl)
          ru.elka.prj.persitence.DatabaseUseCase is injected at …

0

There are 0 answers