The goal is to implement multiplatform-settings in a KMM application.
Tasks:
- Use Koin.
- Properly pass the context.
The outcome:
Here is gist or code bellow
@Suppress("unused")
class MainApp : Application() {
override fun onCreate() {
super.onCreate()
initKoin()
}
private fun initKoin() {
startKoin {
androidContext(this@MainApp)
modules(
settingsModule,
apiModule,
repositoryModule,
viewModelModule
)
}
}
}
private val settingsModule = module {
single { SPManager.create(get()) }
single { SettingsManager() }
}
shared -> commonMain
class SettingsManager: KoinComponent {
private val settings: Settings by inject()
private val favoritesKey = "favorites"
fun toggleFavorite(postId: String) {
val favorites = getFavorites().toMutableSet()
if (favorites.contains(postId)) {
favorites.remove(postId)
} else {
favorites.add(postId)
}
saveFavorites(favorites)
}
fun getFavoritesList(posts: List<PostUiModel>): List<PostUiModel> {
val favoritesString = settings.getString(favoritesKey, "")
val favoritesIds = if (favoritesString.isNotEmpty()) {
favoritesString.split(",").toSet()
} else {
emptySet()
}
return posts.filter { post -> favoritesIds.contains(post.id.toString()) }
}
private fun getFavorites(): Set<String> {
val favoritesString = settings.getString(favoritesKey, "")
return if (favoritesString.isNotEmpty()) {
favoritesString.split(",").toSet()
} else {
emptySet()
}
}
private fun saveFavorites(favorites: Set<String>) {
val favoritesString = favorites.joinToString(",")
settings.putString(favoritesKey, favoritesString)
}
}
shared -> androidMain
fun SPManager.Companion.create(ctx: Context): Settings {
val sharedPreferences = ctx.getSharedPreferences("fp_settings_pref", Context.MODE_PRIVATE)
return AndroidSettings(sharedPreferences)
}
shared -> commonMain
class SPManager internal constructor(
private val settingsManager: SettingsManager,
) : KoinComponent {
companion object
}
vm.kt
class PostViewModel : CoroutineViewModel(), KoinComponent {
private val postRepository: PostRepository by inject()
private val settingsManager: SettingsManager by inject()
- SPManager itself doesn't do what it's supposed to, and I believe the wrapper is unnecessary. Can we do without it?
- Ideally, we should create suspend functions to access the methods of SettingsManager. However, considering that I'm using Settings in the ViewModel afterward, is it really necessary?
- I used the com.russhwolf version: multiplatform-settings:0.7.7 because when I tried to upgrade to version 1, it required raising the Kotlin version, which caused issues. I'll address this in the next iteration.
That's all I wanted to share. If you notice any eye-catching comments or have suggestions, please feel free to share, as the solutions are far from ideal. However, I'm determined to figure out the best way to do this.