RealmList with @Parcelize annotation
Asked Answered
A

1

4

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

Arciniega answered 23/3, 2018 at 17:51 Comment(0)
C
8

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
}
Chat answered 23/3, 2018 at 18:40 Comment(13)
Amazing, you're amazing ! I'm implementing it right now. I just have a question. We won't have access to cars or name variables in the companion object. How to solve this ?Arciniega
You do because Parceler gives you Garage.write and otherwise you are creating a new instance of it (i kinda had a copy paste error there a sec ago) --- but I just realized I can probably also improve this answer a bit using a Parceler<RealmList<T>> with @WriteWith<RealmListParceler>() - assuming I can get its generic to work, anywaysChat
Hmm I can't seem to make object RealmListParceler<T>: because <T> is not allowed on object, not sure if I can make it better then.Chat
What I meant is your code won't compile because the variable cars in parcel.writeRealmList(cars) is not accessible within the companion object.Arciniega
Yes it is accessible, because it is Garage.write(.Chat
Oh sorry, did't saw itArciniega
Thanks a lot you're amazing. It's working like a charm !Arciniega
@AlexandreNussbaumer thanks for accepted, however this way you need to write Parcelable implementation (albeit nicer). Can you try the edited answer if that also works (it's at the top)? I was tinkering with that for the past 20 mins and I think it should work too.Chat
I really like your new cleaner implementation. Just added your code but it doesn't compile. Actually in your interface ´RealmListParceler<T>: Parceler<RealmList<T>?>´, ´RealmList<T>?´ is not within its bounds, it should be a ´Parcelable´ and for the ´@WriteWith´, it says PARCELABLE_TPYPE_NOT_SUPPORTED for ´@Parcelable´ do you have any suggestions ?Arciniega
Both Car and Garage must be Parcelable. Also, I had to make the Parcel.write... and Parcel.read... extension functions be top-level functions, along with the interface and the object. Also, there is no such thing as @Parcelable, it's @Parcelize..... But I've just added this into my test project for this var parents: @WriteWith<PersonRealmListParceler> RealmList<Person> = RealmList() and it works, so I think you are missing a > somewhereChat
So I tried out what I wrote and it worked, I can't tell from a distance what went wrong on your side :|Chat
I implemented your logic again and it does work now, I don't know why it wasn't building before. Again, many thanks!Arciniega
@Chat thanks for this. I am using it but now am getting multiple errors in writeToParcel(Unknown Source:251). I think it's coming from a model using LinkingObjects. How do I exclude LinkingObjects from the parcel? I am using Kotlin ParcelizeLintwhite

© 2022 - 2024 — McMap. All rights reserved.