Abhishek Sharma
12/31/2024, 5:06 PMKoin.kt
class under commonMain
package and declared a module like this
val dataStoreModule: Module = module {
// Provide the coroutine scope
single { CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO> + SupervisorJob()) }
// Provide the DataStore
single {
DataStoreProvider(
coroutineScope = get(),
migrations = listOf(),
context = get()
)
}
// Provide DataStore using DataStoreProvider
single<DataStore<Preferences>> { get<DataStoreProvider>().provide() }
}
Now, under the androidMain
in my Application class, I have done this
class MyApp: Application() {
override fun onCreate() {
super.onCreate()
initKoin()
}
private fun initKoin() {
startKoin {
androidContext(this@MyApp)
modules(
dataStoreModule,
useCaseModule,
viewModelModule
)
}
}
}
But somehow, the context is not getting injected correctly. Any help appreciated.Giorgi
12/31/2024, 7:33 PMGiorgi
12/31/2024, 7:35 PMSanlorng
01/01/2025, 3:21 AMGiorgi
01/01/2025, 1:47 PMGiorgi
01/01/2025, 1:47 PMGiorgi
01/01/2025, 1:48 PMSanlorng
01/01/2025, 3:32 PMGiorgi
01/01/2025, 5:49 PMGiorgi
01/01/2025, 5:49 PMGiorgi
01/01/2025, 5:49 PMSanlorng
01/01/2025, 11:42 PMAbhishek Sharma
01/02/2025, 8:48 AMclass DataStoreProvider(
private val coroutineScope: CoroutineScope,
private val migrations: List<DataMigration<Preferences>>,
private val context: Any? = null
) {
fun provide(): DataStore<Preferences> = dataStorePreferences(
coroutineScope = coroutineScope,
migrations = migrations,
context = context
)
}
expect fun dataStorePreferences(
coroutineScope: CoroutineScope,
migrations: List<DataMigration<Preferences>>,
context: Any? = null,
): DataStore<Preferences>
internal fun createDataStore(
coroutineScope: CoroutineScope = CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO> + SupervisorJob()),
migrations: List<DataMigration<Preferences>>,
context: Any? = null,
path: (context: Any?) -> String,
) = PreferenceDataStoreFactory.createWithPath(
scope = coroutineScope,
migrations = migrations,
produceFile = {
path(context).toPath()
}
)
and the actual implementation for Android is as follow:
actual fun dataStorePreferences(
coroutineScope: CoroutineScope,
migrations: List<DataMigration<Preferences>>,
context: Any?,
): DataStore<Preferences> = createDataStore(
coroutineScope = coroutineScope,
migrations = migrations,
path = { mCtx ->
if(mCtx == null) {
throw IllegalStateException("You must provide context for Android")
}
else
File(appContext.filesDir, "datastore/$DATA_STORE_FILE_NAME").path
}
)
and iOS doesn't need the context as I said above.Giorgi
01/02/2025, 9:09 AMval koin
in BaseActivity. The correct way would be to extend KoinComponent, which has getKoin()
which is pretty much same. So maybe you could remove it.
for creating a DataStore, or any class that needs context on Android here how I usually do.
first I create expect val dataStoreModule: Module
, and implement this in Android and iOS source set accordingly. For example, for Android:
actual val dataStoreModule: Module = module {
single {
DataStoreFactory.create(
storage = OkioStorage<UserEntity>(
fileSystem = FileSystem.SYSTEM,
serializer = UserEntitySerializer,
producePath = {
val appContext = get<Context>().applicationContext
val dataStoreFile = appContext.filesDir.resolve(dataStoreUserFileName)
dataStoreFile.absolutePath.toPath()
},
),
)
}
}
and for iOS
actual val dataStoreModule: Module = module {
single {
DataStoreFactory.create(
storage = OkioStorage<UserEntity>(
fileSystem = FileSystem.SYSTEM,
serializer = UserEntitySerializer,
producePath = {
val dataStoreFile = documentDirectory() + "/" + dataStoreUserFileName
dataStoreFile.toPath()
},
),
)
}
}
They both result in returning same class so now whichever class needs it will have argument like
class UserDataStore(
@Named(IO)
private val ioDispatcher: CoroutineDispatcher,
private val userStore: DataStore<UserEntity>
) {
Also dont forget to include this dataStoreModule
module in startKoin
call.Sanlorng
01/02/2025, 10:13 AM