Just yesterday (2017–10–03), Realm’s latest version was released! In fact, it’s the release candidate of the new major version: 4.0.0-RC1.

What breaks? If you ask me, breaking changes primarily affect the RxJava 1.x users, and those who use Sync Realms— otherwise, most people are safe from these changes.

So, what’s new?

RealmList<Primitives> is now supported!

Note: in 4.0.0-RC1, queries for these fields such as contains("stringList", value) are NOT yet supported.

By primitives, the following types are meant: Boolean/boolean , Long/long , Integer/int , Short/short , Byte/byte , Double/double , Float/float , String , byte[] , and Date .

So yes, prepare for those RealmList<String> s, and purge the RealmList<RealmString> s along with its RealmList<RealmInt> counterparts, they were terrible for the schema anyways! :D

Calling distinct() on a RealmResults will retain its query definition and sort description

This was primarily a breaking change from a Core perspective:

Compound sort and distinct queries Sort and distinct are now applied in the same order that the user called them. This allows for queries where .sort().distinct() is not the same as .distinct().sort() and also lets users construct queries in logical pieces by chaining sort and distinct calls.

Now it’s possible to call distinct() on a filtered/sorted result set and still retain the sort, so you can do:

realm.where(ChatMessage.class)

.findAllSorted(ChatMessageFields.TIMESTAMP, Sort.DESCENDING)

.where()

.distinct("senderId"); // sorted by timestamp when distinct()

And you can also call distinct() multiple times, and it’ll combine these calls and reduce the result set further.

While somewhat an uncommon case, there were a few times where this was the limiting factor, so it’s nice to see an improvement.

You can now access the Realm instance that belongs to a managed RealmObject

Added static RealmObject.getRealm(RealmModel) , RealmObject.getRealm() and DynamicRealmObject.getDynamicRealm() .

It’s also mentioned in the responses below, and I’ve seen people ask for it before.

Personally I haven’t had a need for it, but you could do it in Realm-Swift, so why not in Realm-Java? Well, you can now!

RxJava1 support dropped — RxJava2 and RealmResults. asFlowable()

About time to ditch the realmResults.asObservable() that returned rx.Observable , right?

Thankfully, now RealmResults can be directly exposed as Flowable using realmResults.asFlowable() . Of course, don’t forget that this is pretty much a wrapper around a RealmChangeListener , so this method can only be called on looper threads, and you should not expect it to terminate!

You can read more about this RxJava2 integration, how it works behind the scenes, and how you can use it — in my article titled Creating a Reactive Data Layer using Realm and RxJava2.

So what breaks?

SYNC: RMP 1.x -> RMP 2.x requires the new version of the Realm Object Server (2.x) — which introduces Partial Sync

SYNC: Management Realm is replaced with PermissionManager

SYNC: SyncUser.retrieveUser() replaced by SyncUser.retrieveInfoForUser()

replaced by RealmResults.distinct() is replaced with RealmResults.where().distinct()

is replaced with the aforementioned behavior change in distinct() , although I doubt anyone was truly relying on the sort being cleared by it

, although I doubt anyone was truly relying on the sort being cleared by it RxJava1 support was removed entirely, replaced with RxJava2 support

Some other naming changes (access token -> refresh token, removeChangeListeners() -> removeAllChangeListeners() ), see here for the full list of changes

And:

Realm files upgraded by 4.0.0-RC1 and above cannot be opened by older Realm versions.

That’s pretty much it! Nothing too major, despite the major version increase.

Nothing as hard to wrap your head around as the introduction of collection snapshots, for example.

Conclusion

There were actually lots of improvements in the core behind the scenes, so I actually recommend upgrading from Realm 3.x to Realm 4.x as soon as able.

Just be aware that the file format change also means no going back, so testing things out first never hurts!

For example, if you use Jackson or any other reflection-based parser affected by having to add package rx; and inside it a dummy class public class Observable {} then it is likely you’ll need to update that dummy class to package io.reactivex; , and add Flowable as well.