0

I am developing an Android application using Firebase Realtime Database.

According to Firebase document,

Pass a custom Java object, if the class that defines it has a default constructor that takes no arguments and has public getters for the properties to be assigned.

So I made a custom Kotlin data class with Instant field as below :

import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable

@Serializable
data class UserStatus(
// ...
   val updatedAt: Instant = Clock.System.now(),
// ...
}

It has default constructor and only public getters.

val newUserStatus = UserStatus()
Firebase.database.reference.child(REFERENCE_USER_STATUS_NAME).child(createdUser.uid).setValue(newUserStatus).await()

But, because of Instant field of the class, the error occurs as below :

com.google.firebase.database.DatabaseException: Invalid key: value$kotlinx_datetime. Keys must not contain '/', '.', '#', '$', '[', or ']'
at com.google.firebase.database.core.utilities.Validation.validateWritableKey(Validation.java:123)
at com.google.firebase.database.core.utilities.Validation.validateWritableObject(Validation.java:106)
at com.google.firebase.database.core.utilities.Validation.validateWritableObject(Validation.java:107)
at com.google.firebase.database.DatabaseReference.setValueInternal(DatabaseReference.java:283)
at com.google.firebase.database.DatabaseReference.setValue(DatabaseReference.java:159)
//...

When I comment that Instant field and build, It works well. What am I missing?

Instant has a default serializer.

I figured out that Firebase uses an internal property of kotlinx.datetime.Instant so the key includes $.

When I asked the same question on kotlinx.datetime repository, the maintainer answered me he thinks this problem is related to the Firebase, not Instant.link

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
Jed Choi
  • 35
  • 7

2 Answers2

0

The question is, why? You have to understand that you are trying to save a complex Instant object which is obviously not compatible with the RealTimeDatabase saving standard. According to the documentation, you should simplify the database structure as much as possible.

val updatedAt: String = Clock.System.now().toString()

or

val updatedAt: Long = System.currentTimeMillis()

Wouldn't it be less expensive?

-Hugs

kllysmman
  • 41
  • 1
  • 5
0

As you already noticed, the error that you get is because of the following line of code:

val updatedAt: Instant = Clock.System.now()

And this is because now() returns an object of type Instant. Even if such a field has a default serializer and also exists in a class that is Serializable, it will always produce an error because such an object is not a supported data type.

While kllysmman's solution might work, as it will save a long value in the database, it will more elegant to save the date and time in the Realtime Database as a timestamp. To achieve that, please check my answer in the following post:

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193