# Realm Kotlin 0.4.1 Announcement

In this blogpost we are announcing v0.4.1 of the Realm Kotlin Multiplatform SDK. This release contains a significant architectural departure from previous releases of Realm Kotlin as well as other Realm SDK’s, making it much more compatible with modern reactive frameworks like Kotlin Flows. We believe this change will hugely benefit users in the Kotlin ecosystem.

> **Build better mobile apps with Atlas Device Sync**: 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. Get started now by build: Deploy Sample for Free!

## **Some background**
The Realm Java and Kotlin SDK’s have historically exposed a model of interacting with data we call Live Objects. Its primary design revolves around database objects acting as Live Views into the underlying database. 

This was a pretty novel approach when Realm Java was first released 7 years ago. It had excellent performance characteristics and made it possible to avoid a wide range of nasty bugs normally found in concurrent systems.

However, it came with one noticeable drawback: Thread Confinement.

Thread-confinement was not just an annoying restriction. This was what guaranteed that users of the API would always see a consistent view of the data, even across decoupled queries. Which was also the reason that Kotlin Native adopted a similar memory model

But it also meant that you manually had to open and close realms on each thread where you needed data, and it was impossible to pass objects between threads without additional boilerplate. 

Both of which put a huge burden on developers.

More importantly, this approach conflicts with another model for working with concurrent systems, namely Functional Reactive Programming (FRP). In the Android ecosystem this was popularized by the RxJava framework and also underpins Kotlin Flows.

In this mode, you see changes to data as immutable events in a stream, allowing complex mapping and transformations. Consistency is then guaranteed by the semantics of the stream; each operation is carried out in sequence so no two threads operate on the same object at the same time. 

In this model, however, it isn’t uncommon for different operations to happen on different threads, breaking the thread-confinement restrictions of Realm. 

Looking at the plethora of frameworks that support this model (React JS, RxJava, Java Streams, Apple Combine Framework and Kotlin Flows) It becomes clear that this way of reasoning about concurrency is here to stay.

For that reason we decided to change our API to work much better in this context.

## The new API

So today we are introducing a new architecture, which we internally have called the Frozen Architecture. It looks similar to the old API, but works in a fundamentally different way.

Realm instances are now thread-safe, meaning that you can use the same instance across the entire application, making it easier to pass around with e.g. dependency injection.

All query results and objects from the database are frozen or immutable by default. They can now be passed freely between threads. This also means that they no longer automatically are kept up to date. Instead you must register change listeners in order to be notified about any change.

All modifications to data must happen by using a special instance of a `MutableRealm`, which is only available inside write transactions. Objects inside a write transaction are still live.

## Opening a Realm

Opening a realm now only needs to happen once. It can either be stored in a global variable or made available via dependency injection.

```
// Global App variable
class MyApp: Application() {
   companion object {
       private val config = RealmConfiguration(schema = setOf(Person::class))
       public val REALM = Realm(config)
   }
}

// Using dependency injection
val koinModule = module {
   single { RealmConfiguration(schema = setOf(Person::class)) }
   single { Realm(get()) }
}

// Realms are now thread safe
val realm = Realm(config)
val t1 = Thread {
   realm.writeBlocking { /* ... */ }
}
val t2 = Thread {
   val queryResult = realm.objects(Person::class)
}

```

You can now safely keep your realm instance open for the lifetime of the application. You only need to close your realm when interacting with the realm file itself, such as when deleting the file or compacting it.

```
// Close Realm to free native resources
realm.close()
```

## Creating Data
You can only write within write closures, called `write` and `writeBlocking`. Writes happen through a MutableRealm which is a receiver of the  `writeBlocking` and `write` lambdas. 

Blocking:

```
val jane = realm.writeBlocking { 
   val unmanaged = Person("Jane")
   copyToRealm(unmanaged)
}
```

Or run as a suspend function. Realm automatically dispatch writes to a write dispatcher backed by a background thread, so launching this from a scope on the UI thread like `viewModelScope` is safe:

```
CoroutineScope(Dispatchers.Main).launch {

   // Write automatically happens on a background dispatcher
   val jane = realm.write {
       val unmanaged = Person("Jane")
       // Add unmanaged objects
       copyToRealm(unmanaged)
   }

   // Objects returned from writes are automatically frozen
   jane.isFrozen() // == true

   // Access any property.
   // All properties are still lazy-loaded.
   jane.name // == "Jane"
}
```

## **Updating data**

Since everything is frozen by default, you need to retrieve a live version of the object that you want to update, then write to that live object to update the underlying data in the realm.

```
CoroutineScope(Dispatchers.Main).launch {
   // Create initial object   
   val jane = realm.write {
       copyToRealm(Person("Jane"))
   }
  
   realm.write {
       // Find latest version and update it
       // Note, this always involves a null-check
       // as another thread might have deleted the
       // object.
       // This also works on objects without
       // primary keys.
       findLatest(jane)?.apply {
           name = "Jane Doe"
       }
   }
}
```

## Observing Changes

Changes to all Realm classes are supported through Flows. Standard change listener API support is coming in a future release. 

```
val jane = getJane()
CoroutineScope(Dispatchers.Main).launch {
   // Updates are observed using Kotlin Flow
   val flow: Flow = jane.observe()
   flow.collect {
       // Listen to changes to the object
       println(it.name)
   }
}
```

As all Realm objects are now frozen by default, it is now possible to pass objects between different dispatcher threads without any additional boilerplate:

```
val jane = getJane()
CoroutineScope(Dispatchers.Main).launch {

   // Run mapping/transform logic in the background
   val flow: Flow = jane.observe()
       .filter { it.name.startsWith("Jane") }
       .flowOn(Dispatchers.Unconfined)

   // Before collecting on the UI thread
   flow.collect {
       println(it.name)
   }
}
```

## Pitfalls

With the change to frozen architecture, there are some new pitfalls to be aware of:

Unrelated queries are no longer guaranteed to run on the same version.

```
// A write can now happen between two queries
val results1: RealmResults = realm.objects(Person::class)
val results2: RealmResults = realm.objects(Person::class)

// Resulting in subsequent queries not returning the same result
results1.version() != results2.version()
results1.size != results2.size

```
We will introduce API’s in the future that can guarantee that all operations within a certain scope are guaranteed to run on the same version. Making it easier to combine the results of multiple queries.

Depending on the schema, it is also possible to navigate the entire object graph for a single object. It is only unrelated queries that risk this behaviour. 

Storing objects for extended periods of time can lead to Version Pinning. This results in an increased realm file size. It is thus not advisable to store Realm Objects in global variables unless they are unmanaged. 

```
// BAD: Store a global managed object
MyApp.GLOBAL_OBJECT = realm.objects(Person::class).first()

// BETTER: Copy data out into an unmanaged object
val person = realm.objects(Person::class).first()
MyApp.GLOBAL_OBJECT = Person(person.name)

```

We will monitor how big an issue this is in practise and will introduce future API’s that can work around this if needed. It is currently possible to detect this happening by setting `RealmConfiguration.Builder.maxNumberOfActiveVersions()`
 
Ultimately we believe that these drawbacks are acceptable given the advantages we otherwise get from this architecture, but we’ll keep a close eye on these as the API develops further.

## Conclusion 
We are really excited about this change as we believe it will fundamentally make it a lot easier to use Realm Kotlin in Android and will also enable you to use Realm in Kotlin Multilplatform projects.

You can read more about how to get started at https://docs.mongodb.com/realm/sdk/kotlin-multiplatform/. We encourage you to try out this new version and leave any feedback at https://github.com/realm/realm-kotlin/issues/new. Sample projects can be found here.

The SDK is still in alpha and as such none of the API’s are considered stable, but it is possible to follow our progress at https://github.com/realm/realm-kotlin. 

If you are interested about learning more about how this works under the hood, you can also read more here

Happy hacking!