How to Write a Better Data Access Layer with Realm

Realm Swift2: Best practice for model and Realm model class separation

Officially, (on the iOS side at least), there's no established best practice for abstracting the model class logic away from the actual Realm Object subclasses. That being said, I've definitely heard of apps in the past who do do this sort of logic in order to support multiple types of data frameworks.

If I were to do this, I would make a separate object class for each model implementing my own API on how to get/set data properties, and make the Realm object an internal member of this object. This object would then serve as the generic interface between my app's logic and how to save that data to Realm. That way, if I did want to swap out the data framework down the line, I could simply replace my own data object with a new one, but keep the API consistent.

In regards to that error message you posted, in order to ensure data integrity, you cannot modify a Realm object (UI thread or otherwise) unless it's in a write transaction. That being said, you could encapsulate that logic (i.e. open up a Realm write transaction on the current thread) pretty easily within that abstract object.

How to use Realm live objects and keep database layer decoupled from other layers(business, UI, etc)?

After some research, I came to the conclusion that indeed one needs to make a major choice about integrating Realm to an app: Safe integration x Deep Integration.

Safe Integration

Here you will isolate Realm from the rest of the app code. In order to do that, you won't be able to use the auto-updating/zero-copy of Realm. Instead you will have unmanaged copies, to make sure that anywhere the objects are accessed, you won't have problems with different threads accessing the same objects. Testing is easier this way.

Deep Integration

Here you will use Realm to its full potential, lazily loading data when requested. The downside is that other layers of the code will be aware of Realm, in the sense that they will need to make sure that managed objects are not accessed by different threads. This options potentially involves heavy refactoring if you are not starting a new app, but will bring the performance benefits of the zero-copy approach.

My answer is based on these articles: https://medium.com/@Viraj.Tank/realm-integration-in-android-best-practices-449919d25f2f#.ms3nenq0m and https://medium.com/@Viraj.Tank/deep-integration-of-realm-in-android-production-code-part-2-with-mvp-4cf44ab6289d#.qq4p12mtw

Design pattern for Realm persistence

That's a completely solid design pattern. It's pretty common for developers to abstract the data layer APIs way from their code in case they need to switch it out.

In response to your questions:

  • You're correct. Realm object instances are internally cached, so you can easily call let realm = try! Realm() multiple times with very little overhead.
  • Unless you've found a specific reason, it's probably not necessary to call refresh() on the Realm instance every time you use it. Realm instances on the main thread are automatically refreshed on each iteration of the run loop, so you only need to call refresh() if you're expecting changes on a background thread, or need to access changes before the current run loop has completed.
  • 'Better' design patterns is probably a matter of opinion, but from what I've seen from other codebases, what you've got there is already great! :)

What is the best practice of managing realm instance in Clean Architecture?

If you want a clean separation between UI and database layers in your code, and you want to abstract away your database logic so that ideally your activity can call database layer without knowing how that layer is implemented, then Realm is probably not what you're looking for.

Realm objects are tied to realm instances which means that if you retrieve an object from a realm instance and then close that instance (which you must), you can no longer use the object. Which defeats the entire purpose of using Realm.

If you are going to use Realm, you should keep the realm logic closely tied to your activities/services etc, and don't try to hide it in a separate layer, so that you have full control over it.


        .map(new Func1<RealmObject, T>() {
@Override
public T call(RealmObject realmObject) {
Object o = createObjectFromRealm(realmObject);
realm.close();
return o;
}
});

Should I use repository for Realm(ios)

Adding an abstraction layer over the top of database APIs is pretty common. A lot of other developers have wrapped Realm in their own classes in order to hide the API from their business logic code.

There's a couple of considerations in which to be aware:

  1. You need to be carful not to accidentally hurt performance from this. Some users have gone so far as to copy data out of Realm objects into their own objects. This defeats the purpose of Realm's 'zero-copy' mechanism, and so the app now inherently performs worse than using Realm natively.
  2. This is 'pre-emptive work'. You're doing a lot of work up front, just in case you might change your mind down the line. I converted Core Data to Realm in a rather large app a while ago, and it only took a few hours. Trying to architect a 'generic' database solution which you may never end up using sounds like it might not pay off.
  3. You're increasing app complexity. This means more potential for bugs and more rigorous testing would be needed to make sure your API and the database API are in sync.

As long as the new database you'd be moving to consists of managing objects as well (ie Core Data), converting from one database to another isn't usually a lot of work. As such, I'd recommend avoiding unnecessary work until the time when it becomes necessary.

Disclaimer: I work for Realm, but this is my opinion as someone who's shipped personal apps using Core Data, raw SQLite and Realm in the past.

How do I create my .realm file/database using the model I've just made in XCode?

You're on the right track! You're correct in that the default.realm file isn't created until Realm is initialized for the first time in your code.

You do that by calling

let realm = try! Realm()

The first time this is called, Realm will collect all of the Realm model classes you've created in your app, and will create a new default.realm file with the appropriate backing schema for those models.

I'm not sure why you'd have .sqlite files in there as well; that's definitely not Realm's doing. It's possible if you have other dependencies in there that rely on data persistence (An analytics library for example), they're internally creating those files.



Related Topics



Leave a reply



Submit