One of the issues we’re facing when migrating to K...
# multiplatform
n
One of the issues we’re facing when migrating to KMP is that we have *VM*s loaded with 🤖 resource ids. While making a Resource abstraction seems okay at first, using
ApplicationContext
won’t work with all kinds of resources, while view specific
context
would probably cause leaks. How are you guys approaching this?
j
I think you shouldn't use Int there because that concept is pure Android, maybe other platforms use String. And using context there can be a problem: https://medium.com/androiddevelopers/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54
1
Shouldn't be easier just using domain in ViewModels and map to UI in the UI layer so you can share your VM in all platforms
n
Well it depends, I think it sounds reasonable to do the mapping to
UiModels
in the VM, which the View just “renders” (we currently do this directly from databinding or in the fragment/activity). At that point in order to make the VM platform agnostic you’d need some kind of ResourceMapper as I see it
a
j
@alex009 using moko resource in the ViewModel will have the same problem no?
@nrobi the problem is if you are using the context in the ViewModel and you exposes the string and not the ID, if the user change the language or rotates the screen (having differente string for landscape), it will not update the string
n
@Javier yepp, we’re using ids only, but that’s still android specific
j
Yeah, but if you starts to expose Strings from the ViewModel, you can have problems if locale changes, I don't know if there is a solution to avoid this problem without moving the mapping to the UI layer
n
moko-resources look promising though, I guess if you plan on using it directly from databinding, you’ll have to write your own
BindingAdapters
, right @alex009?
j
But it should have the same problem if you are using it inside
AndroidViewModel
👍 1
a
In moko-mvvm already exists all required adapters :)
😍 1
n
Niice 🤩
s
We approach it something like this. Common Code: commonMain has these types:
Copy code
/**
 * Provides all the exposed resources as platform-specific values.
 */
interface ResourceProvider {
    /**
     * Provides all the exposed [StringResource]s.
     */
    val Strings: Strings

    /**
     * Provides all the exposed [PluralResource]s.
     */
    val Plurals: Plurals

    /**
     * Provides all the exposed [ColorResource]s.
     */
    val Colors: Colors

    /**
     * Provides all the exposed [ImageResource]s.
     */
    val Images: Images
}
Copy code
/**
 * Represents a string-resource
 */
expect class StringResource

/**
 * Wraps a [StringResource] around a plain [String].
 */
expect val String.asStringResource: StringResource

/**
 * Represents a plural-resource
 */
expect class PluralResource {
    internal val quantity: Int
}

/**
 * Represents a color-resource
 */
expect class ColorResource

/**
 * Represents an image-resource
 */
expect class ImageResource
Copy code
/**
 * Provides all the exposed resources for this shared common module.
 * E.g. val uiMsg = Resources.Strings.WelcomeMessage
 */
internal val Resources: ResourceProvider get() = Application.resourceProvider
commonMain then defines the actual Resources it needs for its common code: E.g.
Copy code
interface Strings {
    val WelcomeMessage: StringResource
    fun FullName(first: String, last: String): StringResource
    ...
}

interface Images {
    val AppIcon: DrawableResource
    ...
}
Common `actual`s: The androidMain and iosMain will the create the
actuals
for the
StringResource
,
DrawableResource
, etc. Android will wrap `Int`s as resource-ids; iOS will wrap `String`s as dict-keys. Platform Code: The actual Android
app
(Java/Kotlin) and iOS
iOS
(Swift) modules will provide and inject (Koin) the actual implementations of the
ResourceProvider
interface and assigns it to the
Application.resourceProvider
.
a
Very close to moko-resources. But in moko all expect and actuals generated by gradle plugin
d
This touches on why I think MVP with Persistent state, may become preferable over MVVM. The distinction starts to get subtle; but ViewModels - by their nature - are very prescriptive about how something should be viewed. That's okay for iOS and Android, platforms that share the same form-factor with a touch interface... but becomes a barrier to fulfilling Kotlin Multiplatform's potential to reach across Desktop and Web. On those platforms you'll still have similar UI elements to support your user flows (Presentation), but the on-screen structuring of those elements and their resource demands may differ. Keeping resource definitions confined to platform specific View layer reduces the complexity here.. Enums of possible visual states in the Presenter which get optionally mapped to a resource in the View feels like the way to go. Completely understandable if a Mobile-only project doesn't care about any of this; but recent experience with Kotlin/JS has shown me that with some consideration to the above, it's possible to treat development for the browser like another rich Application client - an exciting prospect.