Hello @TheHiddenDuck great seeing you! lol You know me by my other username in the other place.
I commonly go back and forth between both Firebase and Realm, being formerly a Realm TSE I can every interview ends up going in the direction of questions you ask. I’m not only going to answer your questions with the typical marketing “yeah you can, yada yada” but I’m actually going to give you examples.
Anyways, I actually get this question a ton in interviews of Realm vs Firebase, and here’s my take on this and in relation to your concerns:
Sure, I’d be happy to provide examples in C#!
First, regarding Realm’s offline support, you can definitely use Realm to create a fully functional offline app that only syncs data for paid users. To do this, you can create two separate Realms - one for free users and one for paid users - and only sync the paid user’s Realm. Here’s an example of how you could do this:
// Create a local, unsynced Realm for free users
var freeRealmConfig = new RealmConfiguration { IsReadOnly = false };
var freeRealm = Realm.GetInstance(freeRealmConfig);
// Create a synced Realm for paid users
var user = await AuthenticatePaidUser(); // authenticate the user however you choose
var syncConfig = new SyncConfiguration(user, realmServerUrl);
var paidRealm = await Realm.GetInstanceAsync(syncConfig);
With this setup, the freeRealm will only exist on the user’s device and won’t be synced to the server. The paidRealm, on the other hand, will be synced to the server and only be available to authenticated paid users.
If you want to only sync some data for users and keep some on the device, you can use Realm’s partitioning feature to create separate partitions for synced and unsynced data. Here’s an example:
// Create a local, unsynced Realm for all users
var localConfig = new RealmConfiguration { IsReadOnly = false };
var localRealm = Realm.GetInstance(localConfig);
// Create a synced Realm for paid users with a partition key of "paid"
var user = await AuthenticatePaidUser();
var syncConfig = new SyncConfiguration(user, realmServerUrl) { PartitionValue = "paid" };
var paidRealm = await Realm.GetInstanceAsync(syncConfig);
// Create a synced Realm for some data with a partition key of "shared"
var sharedSyncConfig = new SyncConfiguration(user, realmServerUrl) { PartitionValue = "shared" };
var sharedRealm = await Realm.GetInstanceAsync(sharedSyncConfig);
With this setup, the localRealm will only exist on the user’s device and won’t be synced to the server. The paidRealm will be synced to the server and only contain data with a partition key of “paid”, which can be used to store data that only paid users should have access to. The sharedRealm will also be synced to the server, but contain data with a partition key of “shared”, which can be used to store data that should be available to all users.
Regarding the APK size increase when adding Realm to your app, there are a few things you can do to reduce the size. First, you can use APK splits to only include the native libraries for the ABIs that your app supports. Here’s an example:
// In your project file, define the ABIs you support
<ItemGroup>
<SupportedAbis Include="armeabi-v7a" />
<SupportedAbis Include="arm64-v8a" />
<SupportedAbis Include="x86" />
<SupportedAbis Include="x86_64" />
</ItemGroup>
// In your AndroidManifest.xml file, enable APK splits
<manifest ...>
<dist:module dist:onDemand="true" />
<dist:split dist:abi="armeabi-v7a" />
<dist:split dist:abi="arm64-v8a" />
<dist:split dist:abi="x86" />
<dist:split dist:abi="x86_64" />
</manifest>
By using APK splits, you can significantly reduce the size of your app by only including the native libraries for the ABIs that your app actually supports.
Another thing you can do is to leverage Realm’s built-in partitioning feature to separate data between free and paid users. Partitioning allows you to logically separate data in a Realm database and control access to it based on a partition key. In your case, you could use a partition key to separate data for free and paid users, and then only synchronize the data for paid users.
Regarding the size of the bundled native libraries, Realm does offer a feature called “fat APK” splitting, which can significantly reduce the size of the APK. This feature splits the native libraries into multiple APKs, one per CPU architecture, and downloads only the required APK at runtime. This way, users only download the native libraries that they need, instead of downloading everything. You can read more about fat APK splitting in the Realm documentation.
As for offloading some of the duplicate code to the server, MongoDB Atlas Functions can indeed help you with that. Atlas Functions are serverless functions that allow you to run JavaScript code directly in MongoDB Atlas, using triggers like database events, HTTP requests, or scheduled intervals. You can use Atlas Functions to implement your daily aggregation of statistics and exercises, as well as to verify purchases using Google Play and App Store APIs. You can read more about Atlas Functions in the MongoDB documentation.
Finally, building the entire data layer in apps using KMM is definitely a viable option, especially if you are already considering using Kotlin and Realm. KMM allows you to share business logic and data models across multiple platforms, while still using native UI components and frameworks. You can read more about KMM in the Kotlin documentation. Keep in mind, however, that KMM is still a relatively new technology, and you may encounter some limitations or issues as you develop your app.
Regarding your question about verifying purchases on the backend using both Google Play and App Store APIs, it is definitely possible to do so. Both Google Play and App Store provide APIs that allow you to verify purchases made by users in your app. With Google Play, you can use the Google Play Developer API to verify purchases, while with App Store, you can use the StoreKit API.
As for your question about KMM, while KMM is a relatively new technology, it has been gaining popularity and adoption in the mobile development community. KMM provides a way to share code between iOS and Android, allowing for faster development, easier maintenance, and more consistent behavior across platforms. While it may not have out-of-the-box support for Firebase, there are ways to integrate Firebase with KMM, such as using a common Kotlin module. However, as with any new technology, there may be limitations or issues that you may encounter during development.
Overall, it seems like you have a lot of options to consider for adding sync functionality to your app. Firebase and Realm both offer strong offline support, but come with their own tradeoffs and considerations. If you are already familiar with the GCP ecosystem, using MongoDB may require a bit of a learning curve, but it could provide a way to offload some of the duplicate code to the server. Similarly, using KMM may require some additional setup and configuration, but it could provide a way to share code between platforms and speed up development. Ultimately, the best solution will depend on your specific needs and requirements, so it may be helpful to experiment with different options and see which one works best for your use case.
In conclusion, both Firebase and Realm offer strong offline support for mobile apps, but they have different strengths and limitations.
Firebase offers a comprehensive suite of tools and services for app development, including real-time database, authentication, cloud messaging, and more. It provides a straightforward way to add sync functionality to your app, but it may incur costs for anonymous users and may not be as fast as Realm when working offline.
Realm, on the other hand, provides a first-class offline experience and modern APIs such as coroutines and flows. It allows you to keep your app fully functional offline and sync data only for paid users. However, adding Realm to your app can significantly increase the APK size, and it may require more effort to set up compared to Firebase.
If you are familiar with GCP and want to offload some of the duplicate code to the server, MongoDB Atlas Functions can be a good option to consider. It allows you to run serverless functions on Atlas to perform data aggregation and other tasks.
When it comes to building the entire data layer using KMM, it can be a viable option if you are comfortable with Kotlin and want to share code between iOS and Android. Keep in mind, however, that KMM is still a relatively new technology, and you may encounter some limitations or issues as you develop your app.
Ultimately, the best solution will depend on your specific needs and requirements, so it may be helpful to experiment with different options and see which one works best for your use case.
Anything else, feel free to ask.