3

I'm trying to use safe-args with kotlin-multiplatform types, however I keep geting the same issue during run-time when trying to pass serializable data:

Caused by: java.lang.IllegalArgumentException: org.kotlin.mpp.mobile.models.MyModel is not Serializable or Parcelable.

In my nav_host.xml I have the following:

<fragment
    android:id="@+id/aFragment"
    android:name="com.corp.myapp.main.aFragment"
    android:label="aFragment" >
    <action
        android:id="@+id/action_aFragment_to_bFragment"
        app:destination="@id/bFragment"
        app:popUpTo="@id/bFragment"
        app:popUpToInclusive="true">
        <argument
            android:name="myname"
            app:argType="org.kotlin.mpp.mobile.models.MyModel"
            app:nullable="true" />
    </action>
</fragment>

Two approaches I'm using right now where i get the exact same exception.

First one is by using the kotlinx-serialization plugin where i have the following type:

package org.kotlin.mpp.mobile.models

import kotlinx.serialization.Serializable

@Serializable
data class MyModel(val first: String = "", val last: String = "")

Second one I tried by as first one didn't work was to make a platform specific (JVM) implementation with an extension of java.io.Serializable with the following:

commondataModels.kt:

package org.kotlin.mpp.mobile.models

expect class MyModel(first: String, last: String)

actualdataModels.kt:

package org.kotlin.mpp.mobile.models

import java.io.Serializable

actual data class MyModel actual constructor(val first: String, val last: String): Serializable

I'm making the navigation call in my Activity with the generated direction class:

import org.kotlin.mpp.mobile.models.*


val user = MyModel("Bruce","Lee")
host.findNavController().navigate(AFragmentDirections.actionAFragmentToBFragment(user))

Thanks in advance for any advice!

NOTE: I can make everything work by passing a Bundle with the navigate API, however I would like it to work with safe-args.

Lucho
  • 1,455
  • 1
  • 13
  • 25

2 Answers2

2

As CommonsWare suggested, use @Parcelize (and by extension, Parcelable)

enter image description here

The trick is to make sure that Parcelable doesn't break your common code, as this is Android only concept. For this, in your common code add

expect interface Parcelable

as well as:

@UseExperimental(ExperimentalMultiplatform::class) @OptionalExpectation @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.BINARY) expect annotation class Parcelize()

will look like this: enter image description here

Then in your Android source set, typealias in actual:

actual typealias Parcelable = android.os.Parcelable

actual typealias Parcelize = kotlinx.android.parcel.Parcelize

In other source sets (such as iOS), just put this into actual:

actual interface Parcelable

Dmitri
  • 2,563
  • 1
  • 22
  • 30
  • Hi! Thank you for the idea on using Parcelable instead, I gave it a try but I am not able to get the dependencies _android.os.Parcelable_ and _kotlinx.android.parcel.Parcelize_ to work in my MPP module even though I'm including the plugin _com.android.library_ – Lucho Apr 13 '20 at 12:20
  • @Lucho did you include ktx (Kotlin android extensions) - parcelize is part of that – Dmitri Apr 13 '20 at 17:06
  • Yes i did unfortunetly – Lucho Apr 13 '20 at 17:46
  • @Lucho please see here, this is my multiplatform project, using parcelize, all working - build file for the common here, https://github.com/dmitrish/kinsight-multiplatform/blob/master/SharedCode/build.gradle.kts, you can look at the whole project as well (although its pretty big). Some detail is off, the principle is solid and working for me – Dmitri Apr 13 '20 at 18:42
  • After a few tweaks with the gradle script i got it working now, thx for help! I wont set this as answer as the original question was how to get it to work with serialization, i'll give u a +1 though – Lucho Apr 18 '20 at 11:49
  • 1
    glad it worked. it actually does answer your question precisely - you cannot make it work with serialization, period, and using parcelize is the only way to achieve what you want. but up to you whether to accept or not. – Dmitri Apr 18 '20 at 18:37
  • @Lucho what tweaks did you have to make to the Gradle script to get it working? – scottymack Jan 01 '21 at 19:27
0
  1. The new Kotlinx Serializable:

    • This library is designed to work with transmitting data between your app and its backend (cloud server), Not For Transmitting Data Within Android App. See offcial answer here.

  1. The old Java Serializable and Android Parcelable:

    • Java Serializable is slower than Android Parcelable, see this and this.

    • Kotlin Parcelize plugin simplifys the setup of Android Parcelable, see this. Just need the annotaion and extends from it (for being recognized as Custom Parcelable by safe args).

Sam Chen
  • 7,597
  • 2
  • 40
  • 73