Realm Kotlin 1.10.0

Christian Melchior
Realm Blog
4 min readJun 29, 2023

--

We are excited to announce the release of Realm Kotlin 1.10.0 to Maven Central and Gradle Plugin Portal. This release includes support for:

  1. Improved testing of Android apps using JVM tests and Robolectric.
  2. Support for data ingest or “write-only” sync.
  3. A new subscribe() API for making it easier to work with Flexible Sync subscriptions.

Improved JVM Testing for Android

When developing Android apps, you normally have the choice between running slow and heavy instrumentation tests or fast and lightweight unit tests.

In an ideal world, most tests should run as unit tests, but with Realm at the center of the app, this has historically been tricky as it depended on the Android runtime.

This is now changing.

From 1.10.0, when applying the Realm Gradle Plugin, it will configure Android Unit tests to use the JVM variant of Realm Kotlin and thus load the required native code for your platform. This means it is now possible to test any code involving Realm easily in both unit and instrumentation tests.

Writing a unit test running on the JVM is now as simple as writing this:

// Test class located in <projectRoot>/src/test
class Person: RealmObject {
var name: String = ""

}

class UnitTestExample {
@Test
fun useRealm() = runBlocking {
val config = RealmConfiguration.create(schema = setOf(Person::class))
val realm = Realm.open(config)
realm.write {
copyToRealm(Person())
}
assertEquals(1, realm.query<Person>().count().find())
realm.close()
}
}

This also impacts Robolectric, which is now supported without having to stub or mock any Realm code. Writing a Robolectric test looks like this:

// Test class located in <projectRoot>/src/test
class Person: RealmObject {
var name: String = ""

}

@RunWith(RobolectricTestRunner::class)
class RobolectricSampleTest {
@Test
fun useRealm() = runBlocking {
val config = RealmConfiguration.create(schema = setOf(Person::class))
val realm = Realm.open(config)
realm.write {
copyToRealm(Person())
}
assertEquals(1, realm.query<Person>().count().find())
realm.close()
}
}

Data Ingest Support

With this release, we also introduce support for optimized uploading of heavy client-side insert-only workloads to Atlas App services with data ingestion. This is useful for scenarios that do not require conflict resolution like sensor data from IOT applications.

Adding support for Data Ingest is done by adding a model class that is a subclass of the new AsymmetricRealmObject base class.

import io.realm.kotlin.types.AsymmetricRealmObject

class Measurement: AsymmetricRealmObject {
@PrimaryKey
var _id = ObjectId()
var value: Double = 0.0
var serialNumber: String = ""
}

Since asymmetric realm objects are for write-only scenarios, these objects can only be written to Realm using the new insert() extension method:

val user: User = loginUser()
val config = SyncConfiguration.create(
user = user,
schema = setOf(Measurement::class)
)
val realm = Realm.open(config)
realm.write {
insert(Measurement().apply {
value = 3.1415
serialNumber = "AD:BH:DF:EC:BA"
})
}

This one differs from the normal copyToRealm() method because it doesn’t return a reference to the inserted object. It is also not possible to query for these kinds of objects, so this kind of code is not possible:

// Will fail at compile time
realm.query<Measurement>().find()

A Realm file can contain normal, embedded, and asymmetric realm objects in the same schema.

Read more about Data Ingest here.

Subscribe API

Since we announced MongoDB Atlas Flexible Sync we have received some feedback on annoyances with maintaining the connection between Subscriptions and Local Queries. Especially the fact that you need to create a subscription before being able to run a local query on its data.

With Realm Kotlin 1.10.0, we are introducing a new subscribe() extension method that allows you to run a local query and create the subscription automatically in the background.


// This method will not return until the server side data are available on
// the device.
suspend fun loadData(prefix: String): RealmResults<Person> {
return realm.query<Person>("name BEGINSWITH $0", prefix).subscribe()
}

// You can also define how this method should behave when called repeatedly,
// currently we support 3 different modes:
//
// - FIRST_TIME (default): The method will create the Subscription in the
// background and not return until the data is available on the device.
// Subsequent calls will return immediately with the data on the device,
// while updates are downloaded in the background.
// - ALWAYS: The method will always block until the latest data has been
// downloaded from the server.
// - NEVER: The method will create the subscription in the background,
// but will never wait for the data to be transfered, so you need to use
// the `asFlow()` method to see data arriving on the device.
//
// These 3 modes offer different tradeoffs when the device might be without
// connectivity, so please read the documentation for each before choosing
// which to use.
realm.query<Person>().subscribe(
mode = WaitForSync.ALWAYS,
timeout = 30.seconds
)

We are still collecting feedback on this API, so it is currently marked with an @ExperiementalFlexibleSyncApi annotation. We welcome any feedback, so let us know on GitHub or Twitter if this makes things easier for you or if you have suggestions for further improvements.

Happy hacking!

Thanks for reading. You can see the full changelog here.

Start using Atlas Device Sync for free, today. Atlas Device Sync is a fully-managed mobile backend-as-a-service. Leverage out-of-the-box infrastructure, data synchronization capabilities, built-in network handling, and much more to quickly launch enterprise-grade mobile apps.

If you are still using Realm Java in your Kotlin project, you can learn more about how easy it is to migrate to Realm Kotlin here.

Now go forth and build amazing apps with Realm! As always, we’re around on GitHub and Twitter.

--

--