Search code examples
kotlinandroid-jetpack-composekoinandroid-splashscreen

How to inject class into MainActivity with Koin


I want to create a Splash screen and show it as long as the authentication state of the user gets determined. I have a global singleton called AuthStateController which holds my state and some extra functions.
But because the installSplashScreen function is outside of a composable I can't use Koin to inject the AuthStateController class to get access to my loading state.

Below is my MainActivity with all my Koin modules. And the installSplashScreen function.

class MainActivity : ComponentActivity() {
    // not allowed because outside of Composable
    private val authStateController: AuthStateController by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        startKoin {
            androidLogger(if (BuildConfig.DEBUG) Level.ERROR else Level.NONE)
            androidContext(this@MainActivity)
            modules(listOf(appModule, networkModule, viewModelModule, interactorsModule))
        }

        installSplashScreen().apply {
            setKeepVisibleCondition {
                // need AuthStateController instance to determine loading state
                authStateController.state.value.isLoading
            }
        }

        setContent {
                M3Theme {
                    SetupNavGraph()
                }
            }
        }
    }
}

This is the Koin module that provides my AuthStateController class:

val appModule = module {
    single { AuthStateController(get()) }
}

And this is my AuthStateController class which holds my state and some extra functions:

class AuthStateController(
    private val getMeInterceptor: GetMeInterceptor
) {
    val state: MutableState<AuthState> = mutableStateOf(AuthState())

    fun fetchMe() {
        val me = getMeInterceptor.execute().collect(CoroutineScope(Dispatchers.IO)) { dataState ->
            dataState.data?.let {
                state.value =
                    state.value.copy(profile = it, isAuthenticated = true, isLoading = false)
            }
        }
    }

    init {
        val token = settings.getString(Constants.AUTH_TOKEN)
        if (token.isNotBlank()) {
            fetchMe()
            state.value = state.value.copy(authToken = token)
        }
    }
}

How can I get access to the singleton created with Koin inside the MainActivity and use it inside the installSplashScreen function?

Edit

Android Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.sessions_clean.android">

    <uses-permission android:name="android.permission.INTERNET" />
    
    <application
        // app crashes when adding line below
        android:name=".MainApplication"
        android:allowBackup="false"
        android:supportsRtl="true"
        android:theme="@style/Theme.App.Starting"
        android:usesCleartextTraffic="true">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:theme="@style/Theme.App.Starting">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

When I add android:name to the already existing application tag the App crashes instantly.

But when I create a new application tag for the new MainApplication I get errors in the IDE like Attribute android:allowBackup is not allowed here


Solution

  • I think you can startKoin inside the Application

    MainApplication.kt

    class MainApplication : Application() {
    
        override fun onCreate() {
            super.onCreate()
    
            startKoin {
                ...
            }
            
        }
    }
    

    AndroidManifest.xml

    <manifest ...>
    
        <application
            android:name=".MainApplication"
            ...>
            ...
        </application>
    
    </manifest>