Search code examples
androidkotlinrealmparcelableparceler

RealmList with @Parcelize annotation


I'm trying to use the new @Parcelize annotation added with Kotlin 1.1.4 with a Realm objet containing a RealmList attribute.

@Parcelize
@RealmClass
open class Garage(
        var name: String? = null,
        var cars: RealmList<Car> = RealmList()
) : Parcelable, RealmModel

As RealmList is not supported by the annotation and assuming that @Parcelize is there specially to avoid creating methods what would be the solution to support RealmList ?

Thanks in advance


Solution

  • EDIT:

    Using the power of Type Parcelers and @WriteWith annotation, it is possible to create a RealmList type parceler that can handle this scenario for you:

    With the following code:

    fun <T> Parcel.readRealmList(clazz: Class<T>): RealmList<T>?
        where T : RealmModel,
              T : Parcelable = when {
        readInt() > 0 -> RealmList<T>().also { list ->
            repeat(readInt()) {
                list.add(readParcelable(clazz.classLoader))
            }
        }
        else -> null
    }
    
    fun <T> Parcel.writeRealmList(realmList: RealmList<T>?, clazz: Class<T>)
        where T : RealmModel,
              T : Parcelable {
        writeInt(when {
            realmList == null -> 0
            else -> 1
        })
        if (realmList != null) {
            writeInt(realmList.size)
            for (t in realmList) {
                writeParcelable(t, 0)
            }
        }
    }
    

    You can define an interface like this:

    interface RealmListParceler<T>: Parceler<RealmList<T>?> where T: RealmModel, T: Parcelable {
        override fun create(parcel: Parcel): RealmList<T>? = parcel.readRealmList(clazz)
    
        override fun RealmList<T>?.write(parcel: Parcel, flags: Int) {
            parcel.writeRealmList(this, clazz)
        }
    
        val clazz : Class<T>
    }
    

    Where you'll need to create a specific parceler for the RealmList<Car> like this:

    object CarRealmListParceler: RealmListParceler<Car> {
        override val clazz: Class<Car>
            get() = Car::class.java
    }
    

    but with that, now you can do the following:

    @Parcelize
    @RealmClass
    open class Garage(
            var name: String? = null,
            var cars: @WriteWith<CarRealmListParceler> RealmList<Car> = RealmList()
    ) : Parcelable, RealmModel
    

    And

    @Parcelize
    @RealmClass
    open class Car(
        ..
    ): RealmModel, Parcelable {
        ...
    }
    

    This way you don't need to manually write the Parceler logic.



    ORIGINAL ANSWER:

    Following should work:

    @Parcelize
    open class Garage: RealmObject(), Parcelable {
        var name: String? = null
        var cars: RealmList<Car>? = null
    
        companion object : Parceler<Garage> {
            override fun Garage.write(parcel: Parcel, flags: Int) {
                parcel.writeNullableString(name)
                parcel.writeRealmList(cars)
            }
    
            override fun create(parcel: Parcel): Garage = Garage().apply {
                name = parcel.readNullableString()
                cars = parcel.readRealmList()
            }
        }
    }
    

    As long as you also add following extension functions:

    inline fun <reified T> Parcel.writeRealmList(realmList: RealmList<T>?)
        where T : RealmModel,
              T : Parcelable {
        writeInt(when {
            realmList == null -> 0
            else -> 1
        })
        if (realmList != null) {
            writeInt(realmList.size)
            for (t in realmList) {
                writeParcelable(t, 0)
            }
        }
    }
    
    inline fun <reified T> Parcel.readRealmList(): RealmList<T>?
        where T : RealmModel,
              T : Parcelable = when {
        readInt() > 0 -> RealmList<T>().also { list ->
            repeat(readInt()) {
                list.add(readParcelable(T::class.java.classLoader))
            }
        }
        else -> null
    }
    

    and also

    fun Parcel.writeNullableString(string: String?) {
        writeInt(when {
            string == null -> 0
            else -> 1
        })
        if (string != null) {
            writeString(string)
        }
    }
    
    fun Parcel.readNullableString(): String? = when {
        readInt() > 0 -> readString()
        else -> null
    }