Had anyone success with using Room over Android multi-module Kotlin setup.
#Entity
data class School(#Embedded val student: Student)
data class Student(val age: Int = 0)
Whenever I have both above classes in main module everything compiles properly.
But if I move the Student class to another android library module and School in main module. It throws compile time error as:
error: Entities and Pojos must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type).
Tried the following constructors but they failed to match:
Student(int) : [arg0 : null]
Note: On debug found this might be name mangling issue. If I change the Student class to data class Student(val arg0: Int = 0) it compiles fine.
Looks like at compile time age exposed as arg0
Any idea how to resolve this issue?
I had the same issue. Entities and Room DAO placed in same module everything ok, putting entities in separate module break compilation. In my case the problem was putting enum declaration in same file as entity. Declaring enum in separate file solved the problem.
Related
I am working on adding data persistence to an app using the Room library, based on this documentation. My database only has one column where a String corresponding to a user-selected radio button is to be placed. However, as soon as I attempt to insert a new object (in this case, an Attempt), it gives me the following error:
java.lang.ClassCastException: android.app.Application cannot be cast to com.example.mathreps.MathRepsApplication.
I believe this error comes from the following block of code within my Rating fragment:
private val dataViewModel: MathRepsViewModel by activityViewModels {
MathRepsViewModel.MathRepsViewModelFactory(
(activity?.application as MathRepsApplication).database
.attemptDao()
)
}
Which relates to a different class called MathRepsApplication
class MathRepsApplication: Application() {
val database: AttemptRoomDatabase by lazy {AttemptRoomDatabase.getDatabase(this)}
}
Since this problem most likely relates to multiple classes and packages, here is the link to the repository for the project.
My attempts to fix this have been unsuccessful, with them mostly consisting of heavily reviewing the database implementation and not much else, as I don't exactly know where to start.
In a project, I have a Parcelize-annotated data class that contains a property whose type is a value class. E.g.:
#Parcelize
data class Example(val id: Id) : Parcelable
#JvmInline
value class Id(val long: Long)
With Kotlin 1.6.10, this compiles and works. However, after updating to Kotlin 1.7.20, I started getting the following error a compile time:
Type is not directly supported by 'Parcelize'. Annotate the parameter type with '#RawValue' if you want it to be serialized using 'writeValue()'
I'm not sure I understand why this stopped compiling, but is using #RawValue the actual solution, or is there a better one? It feels like a workaround that could break at runtime if the wrapped type changes to one that isn't Parcelable.
I'm trying to use a room entity with a value class:
#JvmInline
value class UserToken(val token: String)
and the entity:
#Entity(tableName = TABLE_AUTH_TOKEN)
data class TokenEntity(
#PrimaryKey val id: Int = 0,
val token: UserToken
)
I get the following error:
error: Entities and POJOs must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type).
public final class TokenEntity {
^
is it even possible to use room with value class? I couldn't find anything about this. thanks
See the comment from #CommonsWare. Android does not yet support value classes for Room.
The same holds true for the value classes introduced in kotlin 1.5. The type is not supported.
— Support Inline class in Room entity
Here is a possible explanation according to Kotlin Inline Classes in an Android World.
Looking to solve this you could try and add a TypeConverter for your Inline class, but since your Inline class is just the value it wraps when it’s compiled, this doesn’t make much sense and it doesn’t work as you’d expect even if you tried...
I’m just guessing it’s because this is a TypeConverter converting UserId to Int which is basically the same as Int to Int 😭. Someone will probably solve this problem, but if you have to create a TypeConverter for your Inline class then you are still plus one class for the count (multidex). 👎
I think yes if you can provide a type converter for it to change it to some sort of primitive data type (int , string, long ...etc) when it needs to be stored, and to change it back to its class type when it's fetched from database.
You can read about Type Converters from here
Referencing complex data using Room
other than that, your other class should be an entity and bind both your entities together using a Relation.
at least that's what I know about how to use Room.
UserToken always will have only one attribute? In this case, you don't need two classes, just use token: String directly on your entity class;
If you really need keep this class, you have two options:
TypeConverter, where you basically will convert the object into a json, and save as string in the database;
Relation, where you will transform the UserToken in a entity, and on TokenEntity save the tokenId.
I am using realm in our iOS and Android app. For some reason i want to rename one of my realm object.
Initially we name the object Demo and now I want to change it to RealmDemo
In android we achieved it by using #RealmClass annotation
#RealmClass(name = "Demo")
open class RealmDemo : RealmObject() {
}
On iOS side i am not sure how exactly i can do similar as i did in android.
class RealmDemo: Object {
override static func className() -> String {
"Demo"
}
}
I tried above ^ but getting following error "Terminating app due to uncaught exception 'RLMException', reason: 'Object type 'Demo' not managed by the Realm'"
Two things.
First, You can name an object anything you want and change its name at any time.
HOWEVER, that's a destructive change, and Realm doesn't have any way to know the the newly named object 'is the same object' as the prior object.
How that's handled depends on what the use case is:
If this is a development situation, delete your local Realm files and run the app and the object with the new name will be created automatically.
If this is production then a migration block is needed (as on any platform) to migrate the data from the old object to the new one.
Secondly, The other important thing is the name of the object is now RealmDemo, whereas the prior object is Demo
class RealmDemo: Object {
so technically those are two separate objects. To Realm, you've abandoned the Demo object totally and that's a destructive change. Demo is still hanging around but is not referenced in your code so an error is thrown
On a possibly unrelated note, the className function references Demo
override static func className() -> String {
"Demo"
}
But the object name is RealmDemo.
It's not clear why the className function exists but it's not required or really needed. See the documentation for objects to get a feel for their structure - they may need a Primary Key
Seems like realm does not support className overriding for cocoa/ios.
https://github.com/realm/realm-cocoa/issues/2194
https://github.com/realm/realm-cocoa/issues/6624
What do these annotations mean in this Kotlin code in android?
#SuppressLint("SimpleDateFormat")
fun convertLongToDateString(systemTime: Long): String {
return SimpleDateFormat("EEEE MMM-dd-yyyy' Time: 'HH:mm")
.format(systemTime).toString()
}
#Entity(tablename = "daily_sleep_quality_table")
data class SleepNight(...)
....
....
check this
#
- introduces an annotation
- introduces or references a loop label
- introduces or references a lambda label
- references a 'this' expression from an outer scope
- references an outer superclass
# is a Java annotation, which also supported in Kotlin.
#SuppressLint("SimpleDateFormat")
#SuppressLint is an annotation used by the Android Lint tool. Lint will tell you whenever something in your code isn't optimal or may crash. By passing "SimpleDateFormat" there, you're suppressing all warnings that would tell you if you're using SimpleDateFormat in a wrong way.
#Entity(tablename = "daily_sleep_quality_table")
#Entity is annotation used by SQLite to marks a class as an Entity. If your using that in your class, SQLite will identify your class as an Entity with the specified table name.
Those are Java annotations which kotlin supports
Java annotations are 100% compatible with Kotlin
In your example #Entity
Specifies that the class is an entity. This annotation is applied to the entity class