Multiplatform Settings in KMM with Koin

450 Views Asked by At

The goal is to implement multiplatform-settings in a KMM application.

Tasks:

  1. Use Koin.
  2. 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()
  1. SPManager itself doesn't do what it's supposed to, and I believe the wrapper is unnecessary. Can we do without it?
  2. 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?
  3. 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.

0

There are 0 best solutions below