7

I've seen a few SO posts detailing how you bundle prebuilt Realm files with iOS (Obj-c/swift) and Android (Java), but I can't find any information on bundling with Xamarin from a PCL or shared project; is this possible?

I believe it would require a per-project *.realm file (e.g. copied from a single source at compile time) due to the nuances of how files are distributed in each platform, but that's a small price to pay to be able to access prebuilt data from shared code on both platforms.

My objective is to avoid an initial download process when launching the App for the first time.

kez
  • 575
  • 1
  • 6
  • 14

3 Answers3

20

You can add your pre-populated .realm database to your application iOS bundle as a resource (BundleResource Build Action) and as a raw asset to your Android Asset directory (AndroidAsset Build Action).

Using the Xamarin.Forms-based Journal example from Realm you can add your populated database as a linked item to each native project and copy it at application startup.

Example:

In the iOS "native" project of your Xamarin.Form solution/application, update the FinishedLaunching method to copy your pre-populated database if the users database does NOT exist (i.e. this is the first time the application runs):

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    var prepopulated = "prepopulated.realm";
    var realmDB = "journal.realm";
    var documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
    if (!File.Exists(Path.Combine(documentsPath, realmDB)))
    {
        File.Copy(prepopulated, Path.Combine(documentsPath, realmDB));
    }
    global::Xamarin.Forms.Forms.Init();
    LoadApplication(new App());
    return base.FinishedLaunching(app, options);
}

Note: The same technique can be used in your other "native" projects

Note: A custom-written Xamarin.Forms dependency service is an alternative to doing it this way but the results are the same

Since we are copying the prepopulated.realm to journal.realm within the application document directory, we can tell Realm to open this database instead of creating/using a default.realm one.

In the Xamarin.Form Journal project's JournalEntriesViewModel.cs, update the code to open this journal.realm

public JournalEntriesViewModel()
{
    _realm = Realm.GetInstance("journal.realm");
    Entries = _realm.All<JournalEntry>();
    AddEntryCommand = new Command(AddEntry);
    DeleteEntryCommand = new Command<JournalEntry>(DeleteEntry);
}

Same pre-populated database in the solution linked to different "native" projects:

enter image description here

Community
  • 1
  • 1
SushiHangover
  • 73,120
  • 10
  • 106
  • 165
  • Thanks for the great example including the screenshots! (I am one of the Realm Xamarin authors). – Andy Dent May 29 '16 at 09:20
  • @AndyDent Thanks. So far loving `Realm Xamarin`, awesome work!!! Only used Realm in one large/production `Swift`-based project so far but it was amazing and the query performance over SQLite totally made that app shine. I'll dance a merry-jig if `Akavache` gets a working Realm IO provider https://github.com/akavache/Akavache/pull/292 – SushiHangover May 29 '16 at 09:38
  • Thanks @SushiHangover, this is fantastic. I'm still in the planning stages but I accept this is the solution! – kez May 30 '16 at 11:06
  • Happy to say we are now linking to this answer as the "official" doc on bundling, from https://realm.io/docs/xamarin/0.75.0/#bundling-realm-files – Andy Dent Jun 04 '16 at 02:43
  • @SushiHangover Thank you for your elaborated answer. Unfortunately today is my 3rd day only on all 3 - Mac, Realm and Xamarin. So, I am still figuring out what would be the correct place to put this code. So I am requesting you if you could share a GitHub link of your demo project, it would be very helpful. – Dr. Atul Tiwari Oct 21 '16 at 15:17
  • This works really well for iOS, however trying the same thing in Android does not work. Could you detail the method used for Android as well? The line "Note: The same technique can be used in your other "native" projects" is misleading. – E.Freitas Feb 08 '17 at 14:41
  • @E.Freitas What problem are you having on Android? – SushiHangover Feb 08 '17 at 14:49
  • I was able to read the realm file on android by first placing the 'populated.realm' file in the PCL Resources folder, marking it as an embedded resource, then loading it with the code from this answer: http://stackoverflow.com/a/10412442/466197. Then I was able to save it as the 'default.realm' file by using `File.WriteAllBytes(documentsPath, realmDB);` instead of `File.Copy(prepopulated, Path.Combine(documentsPath, realmDB));`. – E.Freitas Feb 14 '17 at 19:04
3

This answer works for Android, as opposed to the accepted answer which works only for iOS.

  1. Add your prepopulated.realm file to the PCL Resources folder. Mark it as an embedded resource by right clicking and selecting 'Build Action' > 'EmbeddedResource'.

  2. Build the app.

  3. From the command line run the monodic --manifest YourApp.dll on your dll file, which for Debug is found in YourApp/Droid/bin/Debug. Find the line with prepopulated.realm and copy the resource string.

  4. Edit your MainActivity.cs and add the following code right after the base.OnCreate(savedInstanceState); line.

enter image description here

E.Freitas
  • 542
  • 1
  • 4
  • 18
1

For anyone wanting to bundle a realm file on native Xamarin.Android you can use the following code. The bundled realm file can be the same name as the final realm file. Just copy the realm file to the Assets directory and make sure the build action is set as AndroidAsset.

var realmDB = "somerealm.realm";
var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
var filePath = Path.Combine(documentsPath, realmDB);
if (!File.Exists(filePath))
{
    using (var asset = Assets.Open(realmDB))
    using (var destination = File.OpenWrite(filePath))
    {
        asset.CopyTo(destination);
    }
}
Mark Silver
  • 412
  • 6
  • 15