This is not the current version. View the latest documentation
See the Current Limitations.
Realm Xamarin enables you to efficiently write your app’s model layer in a safe, persisted and fast way. Here’s what it looks like:
// Define your models like regular C# classes
public class Dog : RealmObject
{
public string Name { get; set; }
public int Age { get; set; }
public Person Owner { get; set; }
}
public class Person : RealmObject
{
public string Name { get; set; }
public RealmList<Dog> Dogs { get; set; }
}
// Persist your data easily
var realm = Realm.GetInstance();
realm.Write(() =>
{
var mydog = realm.CreateObject<Dog>();
mydog.Name = "Rex";
mydog.Age = 9;
});
// Query it with standard LINQ, either syntax
var r = realm.All<Dog>().Where(d => d.Age > 8);
var r2 = from d in realm.All<Dog>() where d.Age > 8 select d;
Getting Started
Download Realm Xamarin or see the source on GitHub.
Prerequisites
Apps using Realm can target:
- Apple via Xamarin: iOS 7 or later using both native UI and Xamarin Forms.
- Android via Xamarin: API level 10 or later, using both native UI and Xamarin Forms. We support the full range of Android versions supported by Xamarin.
We support the current Xamarin Stable and Beta update channels, at the time of release this corresponded to:
- Xamarin iOS version 9.6.0.0 (with either Xamarin Studio or Visual Studio)
- Xamarin Android version 6.0.2.1 (with either Xamarin Studio or Visual Studio)
- Xamarin Studio version 5.10.3
Installing with NuGet
Installing the NuGet packages
Realm for Xamarin consists of two NuGet packages: Realm and RealmWeaverFody.
For the private beta, you should have been given GitHub credentials to access these packages as raw .nupkg files, zipped into a single package.
After the beta period, the packages will be available in the official NuGet package repository.
Xamarin Studio on OS X
Once-off setup of local source
In order to add these to your project, you need to let Xamarin Studio use a folder as a NuGet repository. You only have to do this once, in the menu Xamarin Studio -> Preferences -> NuGet/Sources.
- Click the Add button, type a name like “RealmLocal”, and set the URL to point to your folder, for example
/Users/John/LocalNugets/
. - Username and Password should be left blank.
Using the Realm Packages
- Now, you should be able to add Realm to an iOS project. Click Add Package on your project,
- Select your RealmLocal (or whatever you named it before) repository in the dropdown in the upper left corner, you should be see both packages.
- Select Realm and add it
- RealmWeaver.Fody will have been installed as a dependency and you should see Fody added as a dependency in turn.
The Realm package contains the fundamentals for Realm. It depends on the RealmWeaver.Fody package which contains a Fody weaver, responsible for turning your RealmObject subclasses into persisted ones.
At this point, you should have the two packages installed. If your project was already using Fody you were asked if you wanted to replace FodyWeavers.xml or not. Whether you did this or not, the important thing is that you end up with a FodyWeavers.xml file that contains all the weavers you want active, including RealmWeaver.
This is an example of how your FodyWeavers.xml file should look if you were not already using any other weavers and added Realm:
<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
<RealmWeaver />
</Weavers>
Visual Studio on Windows
The steps are basically the same as for Xamarin Studio above:
Once-off setup of local source
- Choose Tools - NuGet Package Manager - Package Manaager Settings to open the Options dialog with NuGet Package Manager selected
- Select the Package Sources item
- Press the green cross Add Button to add a source
- Fill in the Name like “RealmLocal”, and set the Source to point to your folder, for example
/Users/John/LocalNugets/
. - Press the Update button so your new source appears in the list of sources.
Using the Realm Packages
- In your IOS project, choose Tools - NuGet Package Manager - Manage Packages for Solution…
- Select your “RealmLocal” Package source: with the drop-down menu in the top right of the panel
- Select the Realm package in the list of available packages
- Check that your project is checked on the right hand side, so the Install button is enabled
- Press Install
- Press OK in the dialog that comes up, which should be telling you it will install Realm, RealmWeaver.Fody and Fody
Realm Browser
We also provide a standalone Mac app named Realm Browser to read and edit .realm databases.
You can generate a test database with sample data using the menu item Tools > Generate demo database.
If you need help finding your app’s Realm file, check this StackOverflow answer for detailed instructions.
The Realm Browser is available on the Mac App Store.
Getting Help
- Need help with your code? Ask on StackOverflow. We actively monitor & answer questions on SO!
- Have a bug to report? Open an issue on our repo. If possible, include the version of Realm, a full log, the Realm file, and a project that shows the issue.
Models
Realm data models are defined using traditional C# classes with properties. Simply subclass RealmObject
to create your Realm data model objects.
Realm model objects mostly function like any other C# objects – you can add your own methods and events to them and use them like you would any other object. The main restrictions are that you can only use an object on the thread which it was created, and persisted properties have generated getters and setters.
Relationships and nested data structures are modeled simply by including properties of the target type or RealmList
for typed lists of objects.
// Dog model
public class Dog : RealmObject
{
public string Name { get; set; }
public int Age { get; set; }
public Person Owner { get; set; }
}
public class Person : RealmObject
{
public string Name { get; set; }
public RealmList<Dog> Dogs { get; set; }
}
Controlling property persistence
Classes which descend from RealmObject
are processed by the Fody weaver at compilation time. All their properties that have automatic setters and getters are presumed to be persistent and have setters and getters generated to map them to the internal Realm storage.
We also provide some C# attributes to add metadata to control persistence.
To avoid a property being made persistent, simply add the [Ignored]
attribute. A common example is using external media, where you could persist the path to a file but not the binary image:
public string HeadshotPath { get; set; }
// Image in memory during run
[Ignored]
public Image Headshot { get; set; }
Custom Setters
Properties which have their own setter or getter implementation are automatically ignored. You can use this to add validation:
private string Email_ { get; set; }
// Validating version of persistent Email_ property
public string Email
{
get { return Email_; }
set
{
if (!value.Contains("@")) throw new Exception("Invalid email address");
Email_ = value;
}
}
Supported Types
Realm supports primitive types except unsigned ones (bool
, char
, byte
, short
, int
, long
, float
and double
) as well as string
, and DateTimeOffset
. The nullable equivalents are supported as well.
Realms
Realms are our equivalent of a database: they contain different kinds of objects, and map to one file on disk.
The Default Realm
You may have noticed so far that we have always initialized our realm variable by calling Realm.GetInstance(string optionalPath)
. This static constructor will return a Realm instance for your thread, that maps to a file called default.realm
located in Environment.SpecialFolder.Personal
.
The file is located at the root of the writable directory for your application. As Realm uses internal storage for the default Realm, your app does not require any read or write permissions. In most cases, you can find the file in the folder /data/data/<packagename>/files/
.
It is important to note that Realm instances are thread singletons, meaning that the static constructor will return the same instance for every thread.
Configuring a Realm
Calling Realm.getInstance(filename)
makes it easy to get started with Realm. For more fine-grained control, it is possible to create a RealmConfiguration
object that controls more aspects of how a Realm is created.
A RealmConfiguration
allows you to control the database path in several ways. You can’t change the path once the configuration has been constructed.
- Change the entire path by passing in a new absolute path.
- Put your realms in a sub-directory of the standard location by passing in a relative path.
- Change the realm name by just passing in a new filename.
You can pass around an instance of a configuration, to open all your realms with the same settings. This is the most common use for opening the same realm in different threads.
You can also override the default configuration to change the defaults without having to pass around an object.
Using a Realm across Threads
The only rule to using Realm across threads is to remember that Realm, RealmObject or RealmList instances cannot be passed across threads.
When you want to access the same data from a different thread, you can obtain a new Realm instance (i.e. Realm.GetInstance(myFilename)
or its cousins) and get your objects through a query. The objects will map to the same data on disk, and will be readable & writeable from any thread!
Closing Realm instances
Realm
implements IDisposable
in order to take care of native memory deallocation and file descriptors so instances will be closed automatically when variables go out of scope.
You can safely call Realm.Close()
at any time, including multiple times, to ensure early closing.
Finding a Realm File
If you need help finding your app’s Realm file, check this StackOverflow answer for detailed instructions.
Auxiliary Realm Files
Alongside the standard .realm
files, Realm also generates and maintains additional files for its own internal operations.
.realm.lock
- A lock file for resource locks..realm.log_a
,.realm.log_b
- Log files for transaction logs..realm.note
- A named pipe for notifications.
These files don’t have any effect on .realm
database files, and won’t cause any erroneous behavior if their parent database file is deleted or replaced.
When reporting Realm issues, please be sure to include these auxiliary files along with your main .realm
file as they contain useful information for debugging purposes.
Class Subsets
In some scenarios you may wish to limit which classes can be stored in a specific Realm.
You can do this by setting the ObjectClasses
property of your RealmConfiguration
:
class LoneClass : RealmObject
{
public string Name { get; set;}
}
var config = new RealmConfiguration("RealmWithOneClass.realm");
config.ObjectClasses = new Type[] {typeof(LoneClass)};
// or specifying two classes in the Realm
config.ObjectClasses = new Type[] {typeof(Dog), typeof(Cat)};
Relationships
RealmObject
s can be linked to each other by using RealmObject
and RealmList
properties.
RealmList
s implement the standard .NET IList
generic interface.
To-One Relationships
For many-to-one or one-to-one relationships, simply declare a property with the type of your RealmObject
subclass.
public class Dog : RealmObject
{
// ... other property declarations
public Person Owner { get; set; };
}
public class Person : RealmObject{}
You can use this property like you would any other:
var jim = realm.CreateObject<Person>()
var rex = realm.CreateObject<Dog>();
rex.Name = "Rex";
rex.Owner = jim;
To break the relationship, simply assign null
:
rex.Owner = null;
When using RealmObject
properties, you can access nested properties using normal property syntax. For example rex.Owner.Address.Country
will traverse the object graph and automatically fetch each object from Realm as needed.
To-Many Relationships
You can define a to-many relationship using RealmList
properties. When you use these properties, you get back a RealmList
which may be empty or contains related RealmObject
s of a single type.
To add a “Dogs” property on our Person
model that links to multiple dogs, we simply declare it as a RealmList<Dog>
property.
public class Dog : RealmObject
{
}
public class Person : RealmObject
{
// ... other property declarations
public RealmList<Dog> Dogs { get; set; }
}
You can access and assign RealmList
properties as usual for an IList
:
jim.Dogs.Add(rex);
Assert(jim.Dogs.Count == 1); // nobody but rex
Optional Properties
Reference types such as string
and related RealmObject
values can be null.
Nullable value types such as int?
are fully supported.
Indexed Properties
Currently only strings and integers can be indexed.
Indexing a property will greatly speed up queries where the property is compared for equality (i.e. the ==
and Contains
operators), at the cost of slower insertions.
To index a property, simply add the [Indexed]
attribute to the property declaration, e.g.:
public class Person : RealmObject
{
[Indexed]
public string Name { get; set; }
public RealmList<Dog> Dogs { get; set; }
}
ObjectId attributes
A single [ObjectId]
attribute can be specified on one property to set the model’s object id. Declaring an object id allows objects to be looked up and updated efficiently and enforces uniqueness for each value.
Only char
’s, integral types and strings can be used as object id’s.
Once an object with an ObjectId is added to a Realm, the object id cannot be changed.
If you come from a traditional database background, you can think of the object id as being very similar to a SQL primary key.
From an object modelling perspective, the object id is like a persistent pointer to an object. You can use the object id to quickly lookup the object to get a reference to it in a different thread.
Note that putting the [ObjectId]
attribute on multiple properties is undefined behaviour and may cause runtime errors or just use one of the attributed properties.
public class Person : RealmObject
{
[ObjectId]
public string SSN { get; set; }
[Indexed]
public string Name { get; set; }
public RealmList<Dog> Dogs { get; set; }
}
Ignored Properties
Use the [Ignored]
attribute to leave a property alone and just treat it as a standard C# property.
If you define a setter or getter function on the property then it is automatically ignored (future feature not included in the beta).
Writes
All changes to an object (addition, modification and deletion) must be done within a write transaction.
To share objects between threads or re-use them between app launches you must persist them to a Realm, an operation which must be done within a write transaction.
Since write transactions incur non-negligible overhead, you should architect your code to minimize the number of write transactions.
Because write transactions could potentially fail like any other disk IO operations, you should be prepared to handle exceptions from writes so you can handle and recover from failures like running out of disk space. There are no other recoverable errors. For brevity, our code samples don’t handle these errors but you certainly should in your production applications.
There are two easy ways to create write transactions: Realm.BeginWrite()
and Realm.Write()
. The first one, Realm.BeginWrite()
returns a Transaction
which implements the Dispose
pattern, allowing you to use it with using
:
using(var transaction = realm.BeginWrite())
{
person.FirstName = "John";
person.Age = 56;
transaction.Commit();
}
Note that you must explicitly Commit
the transaction, or it will automatically be rolled back. The other way is to wrap your mutating code with Realm.Write()
:
realm.Write(() =>
{
person.FirstName = "John";
person.Age = 56;
});
Here, the implicit transaction will be committed per default unless an exception was thrown.
Creating Objects
When you have defined a model you can instantiate your RealmObject
subclass and add the new instance to the Realm. Consider this simple model:
public class Dog : RealmObject
{
public string Name { get; set; }
public int Age { get; set; }
}
You create an instance by calling the Realm.CreateObject<>()
method:
realm.Write(() =>
{
var mydog = realm.CreateObject<Dog>(); // immediately managed by the Realm
mydog.Name = "Rex";
myDog.Age = 10;
});
After you have created the object, all changes you make to it will be persisted (and must be made within a write transaction). Any changes are made available to other threads that use the same Realm when the write transaction is committed.
Please note that writes block each other, and will block the thread they are made on if multiple writes are in progress. This is similar to other persistence solutions and we recommend that you use the usual best-practices for this situation, namely offloading your writes to a separate thread.
Due to Realm’s MVCC architecture, reads are not blocked while a write transaction is open. Unless you need to make simultaneous writes from many threads at once, you should favor larger write transactions that do more work over many fine-grained write transactions.
Updating Objects
You can update any object by setting its properties within a write transaction.
// Update an object with a transaction
using (var trans = realm.BeginWrite())
{
author.Name = "Thomas Pynchon";
trans.Commit();
}
Deleting Objects
Pass the object to be deleted to the Realm.Remove
method within a write transaction.
var cheeseBook = realm.All<Book>().First(b => b.Name == "Cheese");
// Delete an object with a transaction
using (var trans = realm.BeginWrite())
{
realm.Remove(cheeseBook);
trans.Commit();
}
You can also delete all objects stored in a Realm. Note the Realm file will maintain its size on disk to efficiently reuse that space for future objects.
Queries
Queries implement standard LINQ syntax.
You get a basic, typed collection of all objects of a given type using the All
method and then apply Where
or other LINQ operators to get a filtered collection.
Fluent or Extension Syntax
var oldDogs = realm.All<Dog>().Where(d => d.Age > 8);
Query Expression Syntax
var oldDogs = from d in realm.All<Dog>() where d.Age > 8 select d;
Regardless of which syntax you use, the resulting RealmResults
collection conforms to the IQueryable interface so you can further iterate or process it:
foreach (var d in oldDogs)
{
Debug.WriteLine(d.Name);
}
More Query Examples
If you’re not used to LINQ queries, here are some examples of basic queries to get you used to the syntax. Sticking with the Extension Syntax you can see how to write your basic queries.
To extract a list of all users named John or Peter you would write:
var johnsAndPeters = _realm.All<Person>().Where(p =>
p.FirstName == "John" ||
p.FirstName == "Peter");
var peopleList = johnsAndPeters.ToList();
The first statement gives you a new instance johnsAndPeters
of the class RealmResults
. It is an IQueryable
created to find the users with the name John or Peter. This is standard LINQ implementation - you get an object representing the query. The query doesn’t do anything until you make a further call that needs to iterate or count the results.
The ToList
call, in this example, fires off the query which maps straight to the Realm core.
Objects are not copied - you get a list peopleList
of references to the matching objects, and you work directly with the original objects that matches your query.
Instead of retrieving them all into a list, as we saw earlier you could also be iterating through the query results, with the standard C# foreach
statement:
int i = 0;
foreach (var pers in johnsAndPeters) { // iterate query
Assert.That(peopleList[i++], Is.EqualTo(pers)); // check iterator against earlier list
}
Logical Operators
The standard C# logical operators are used in LINQ expressions to compose the query.
That includes the ability to use parentheses to group nested expressions, with the precedence as you’d expect in an if
statement:
var ComplexPeople = _realm.All<Person>().Where(p =>
p.LastName == "Doe" &&
(p.FirstName == "Fred" || p.Score > 35);
Retrieving objects by type
To get all objects of a given type from a Realm, just use realm.All<Person>()
by itself. It returns a RealmResults
of all instances of the model class being queried - Person
in this case.
You can then choose to apply a LINQ query further to restrict the set. Remember, until you start iterating a RealmResults
there’s no overhead.
var ap = _realm.All<Person> (); // this is all of a type
var scorers = ap.Where(p => p.Score > 35); // restrict with first search clause
var scorerDoe = scorers.Where(p => p.LastName == "Doe"); // effectively an AND clause
Notifications
On Android, change listeners only work on Looper threads. For non-looper threads, you manually have to use Realm.Refresh() instead.
If you have a background thread adding data to a Realm, your UI or other threads can get notified of changes in a realm by adding a listener, which is executed when the Realm is changed (by another thread or process):
realm.RealmChanged += () =>
{
// Update UI
}
Sorting
The standard LINQ clauses OrderBy
, OrderByDescending
, ThenBy
and ThenByDescending
can be used to specify a multi-level sort that affects the order of objects supplied by a RealmResults
.
They work via the internal query engine to provide efficient sorted access without loading all objects in the results. You don’t have to perform a query with Where
to be able to use a sort - you can just sort the results from All
.
Warning if you use the ToList
clause to extract a list of objects and then use an OrderBy
you are sorting in-memory using LINQ for Objects. Make sure you only use ToList
at the end of your expression.
var highestScore = _realm.All<Person>().OrderByDescending(p => p.Score).First();
// sorting by two clauses and THEN using ToList to compare in an assertion
// (from a unit test that has duplicate first names)
var sortAD = _realm.All<Person>().Where(p => p.IsInteresting).
OrderBy(p => p.FirstName).
ThenByDescending(p => p.Latitude).ToList();
var sortADname = sortAD.Select(p => p.FirstName);
Assert.That(sortADname, Is.EqualTo( new [] {"John", "John", "Peter"}) );
Encryption
Please take note of the Export Compliance section of our LICENSE, as it places restrictions against the usage of Realm if you are located in countries with an export restriction or embargo from the United States.
The Realm file can be stored encrypted on disk by passing a 64 byte (512-bit) encryption key to RealmConfiguration.EncryptionKey
:
var config = new RealmConfiguration("Mine.realm");
var answerKey = new byte[64]; // key MUST be exactly this size to avoid a FormatException
answerKey[0] = 42; // first byte is 42 followed by 63 zeroes
config.EncryptionKey = answerKey;
var realm = Realm.GetInstance(config); // will create/open encrypted realm "Mine.realm"
This ensures that all data persisted to disk is transparently encrypted and decrypted with standard AES-256 encryption. The same encryption key must be supplied each time a Realm instance for the file is created.
Note that if you specify the wrong key or fail to specify a key for an encrypted realm you get a RealmFileAccessErrorException
when you call GetInstance
.
Current Limitations
Realm is currently in beta and we are continuously adding features and fixing issues while working towards a 1.0 release. Until then, we’ve compiled a list of our most commonly hit limitations.
Please refer to our GitHub issues for a more comprehensive list of known issues.
Features missing from this preview version which are expected to be added prior to release:
- Assigning list values to
RealmList
fields to add multiple relationships in one go - Async queries
- Binary data fields (e.g., storing pictures)
- Cascading deletes
- Collection notifications
- Default values – the standard way of defining them is not compatible with the weaving
- Delete all objects
- Dynamic Realms
IList
used for declaration rather than requiringRealmList
- Indexing
- In-memory Realms
- Migrations
- More LINQ operations
- Searching by related data
- Updating Objects by their object id
General Limits
Realm aims to strike a balance between flexibility and performance. In order to accomplish this goal, realistic limits are imposed on various aspects of storing information in a realm. For example:
- Class names must be between 0 and 63 bytes in length. UTF8 characters are supported. An exception will be thrown at your app’s initialization if this limit is exceeded.
- Property names must be between 0 and 63 bytes in length. UTF8 characters are supported. An exception will be thrown at your app’s initialization if this limit is exceeded.
- Binary data properties cannot hold data exceeding 16MB in size. To store larger amounts of data, either break it up into 16MB chunks or store it directly on the file system, storing paths to these files in the realm. An exception will be thrown at runtime if your app attempts to store more than 16MB in a single property.
DateTimeOffset
properties are stored truncated to seconds precision in our common Realm internal format.- iOS Limitation: The total size of all open Realm files cannot be larger than the amount of memory your application would be allowed to map in iOS — this changes per device, and depends on how fragmented the memory space is at that point in time (there is a radar open about this issue: rdar://17119975). If you need to store more data, you can split into multiple Realm files and open and close them as needed.
FAQ
General
Should I use Realm in production applications?
Realm has been used in production in commercial products since 2012. The Xamarin C# product has been in beta since December 2015, but uses the same C++ core as all other Realm products.
You should expect our C# APIs to change as we evolve the product from community feedback — and you should expect more features & bugfixes to come along as well.
Do I have to pay to use Realm?
No, Realm is entirely free to use, even in commercial projects.
How do you all plan on making money?
We’re actually already generating revenue selling enterprise products and services around our technology. If you need more than what is currently in our releases or in realm-dotnet, we’re always happy to chat by email. Otherwise, we are committed to developing realm-dotnet in the open, and to keep it free and open-source under the Apache 2.0 license.
I see references to a “core” in the code, what is that?
The core is referring to our internal C++ storage engine. It is not currently open-source but we do plan on open-sourcing it also under the Apache 2.0 license once we’ve had a chance to clean it, rename it, and finalize major features inside of it.
I see a network call to Mixpanel when I run my app, what is that?
Realm collects anonymous analytics when you run the Realm assembly weaver on your assemblies containing RealmObject
classes. This is completely anonymous and helps us improve the product by flagging which version of Realm you use and what OS you use, and what we can deprecate support for. This call does not run when your app is in production, or running on your user’s devices — only when your assemblies are woven by Fody during build. You can see exactly how & what we collect, as well as the rationale for it in our source code.
Xamarin-Specific
Failing to Weave
You may see a warning in the build log about classes not having been woven. This indicates that the Fody weaving package is not properly installed.
Firstly, check that the FodyWeavers.xml
file contains an entry for RealmWeaver
.
It is also possible that the installation of Fody has failed. This has been experienced with Visual Studio 2015 and versions of NuGet Package Manager prior to version 3.2. To diagnose this, use a text editor to check that your .csproj
has a line importing Fody.targets
, such as:
<Import Project="..\packages\Fody.1.29.3\build\portable-net+sl+win+wpa+wp\Fody.targets" Condition="Exists('..\packages\Fody.1.29.3\build\portable-net+sl+win+wpa+wp\Fody.targets')" />
Simply upgrading to a later version of NuGet Package Manager should fix this problem.