Getting Started
Prerequisites
We support the following platforms:
- Xamarin.iOS for iOS 7 or later, using native UI or Xamarin Forms
- Xamarin.Android for API level 10 or later, using native UI or Xamarin Forms
- Xamarin.Mac for macOS 10.11 or later, using nativeUI or Xamarin Forms
- .NET Framework 4.6.1 or later for Windows 8.1 or later
- Universal Windows Platform applications using .NET Standard 2.0 or later (Fall Creators Update)
- .NET Core 2.0 or later on:
- Ubuntu 16.04 or later
- Debian 8 or later
- RHEL 7.1 or later
- macOS 10.11 or later
- Windows 8.1 or later
And the following environments:
- Visual Studio 2015 Update 2 or higher
- Visual Studio for Mac 7.0 or higher
Installation
If you’re upgrading from Realm 3.x, please refer to the Upgrade Guide.
You install Realm via NuGet, or you can browse the source code on GitHub.
- On the “Packages” node under your project in the Solution pane, click the gear button and select “Add Packages…”
- Type “Realm” in the search box
- Select Realm and add it
The Realm package contains everything you need to use Realm. It depends on the Fody weaver, responsible for turning your RealmObject subclasses into persisted ones.
If your project was already using Fody, you should add the Realm weaver to the FodyWeavers.xml
file. Otherwise, this file will be created when you first build your project. If you’re using the old .csproj format where all files are explicitly listed in the project file, you will need to add the FodyWeavers.xml
file to your project.
This is an example of how your FodyWeavers.xml file should look if you were not already using any other weavers when you added Realm:
<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
<Realm />
</Weavers>
- In your project, choose Tools - NuGet Package Manager - Manage Packages for Solution…
- 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
The Realm package contains everything you need to use Realm. It depends on the Fody weaver, responsible for turning your RealmObject subclasses into persisted ones.
If your project was already using Fody, you should add the Realm weaver to the FodyWeavers.xml
file. Otherwise, this file will be created when you first build your project. If you’re using the old .csproj format where all files are explicitly listed in the project file, you will need to add the FodyWeavers.xml
file to your project.
This is an example of how your FodyWeavers.xml file should look if you were not already using any other weavers when you added Realm:
<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
<Realm />
</Weavers>
Realm .NET 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 IList<Dog> Dogs { get; }
}
var realm = Realm.GetInstance();
// Use LINQ to query
var puppies = realm.All<Dog>().Where(d => d.Age < 2);
puppies.Count(); // => 0 because no dogs have been added yet
// Update and persist objects with a thread-safe transaction
realm.Write(() =>
{
realm.Add(new Dog { Name = "Rex", Age = 1 });
});
// Queries are updated in realtime
puppies.Count(); // => 1
// LINQ query syntax works as well
var oldDogs = from d in realm.All<Dog>() where d.Age > 8 select d;
// Query and update from any thread
new Thread(() =>
{
var realm2 = Realm.GetInstance();
var theDog = realm2.All<Dog>().Where(d => d.Age == 1).First();
realm2.Write(() => theDog.Age = 3);
}).Start();
Android ABI Support
Due to some instruction set limitations, we do not support the armeabi
ABI setting.
The default templates that create new Xamarin projects currently have all ABI settings checked, for Debug builds, but only armeabi
on Release. You must change this before doing your own Release builds.
If you do not have other ABIs checked, a System.TypeInitializationException
may occur when a user runs on a different device. For example, deploying on a 64-bit device such as a Galaxy Tab S2 will cause that error if you have armeabi
and armeabi-v7a
but not arm64-v8a
checked.
Unless you have a good reason to avoid linking other ABIs, the best thing to do is to check all of them other than armeabi
, so you want checked:
armeabi-v7a
arm64-v8a
x86
x86_64
In Visual Studio for Mac, these settings are from right-clicking a project’s Options - Build - Android Build - Advanced Tab.
In Visual Studio on Windows, these settings are from right-clicking a project’s Properties - Android Options - Advanced Tab.
Realm Studio
Realm Studio is our premiere developer tool, built so you can easily manage the Realm Database and Realm Platform. With Realm Studio, you can open and edit local and synced Realms, and administer any Realm Object Server instance. It supports Mac, Windows and Linux.
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.
Also, note that your class must have a public, parameterless constructor. If you don’t add any constructors, the compiler will add this for you. However, if you add at least one constructor to your class, make sure there is a public parameterless one as well.
Relationships and nested data structures are modeled simply by including properties of the target type or IList
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 IList<Dog> Dogs { get; }
}
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, described further in Optional Properties.
Collections
Realm uses standard types to represent groups of objects:
IQueryable<T>
, a class representing objects retrieved from queries.IList<T>
, a class representing to-many relationships in models.
At runtime, both conform to the IRealmCollection<T> interface which in turn conforms to IReadOnlyList<T>
and INotifyCollectionChanged
and supports lazy enumeration - the size of the collection is known but objects are only loaded into memory as you iterate the collection.
Using collections of supported objects is done by declaring a property of type IList<T>
where T
is any supported type:
public class Image : RealmObject
{
// Other properties...
public IList<string> Tags { get; }
public IList<double?> Ratings { get; }
}
The collection properties emit change notifications (see Collection Notifications for more details) and can contain both primitive types - integers, strings, etc. - and other objects (see To-Many Relationships). Modifying the collection (e.g. adding or removing items) must happen in a write transaction:
var image = ...;
realm.Write(() =>
{
image.Tags.Add("sunset");
image.Ratings.Move(from: 2, to: 5);
});
Date Type
We use the standard DateTimeOffset
type as our main type for representing dates, rather than DateTime
.
This is based on the recommendation from Microsoft due to ambiguities in DateTime
.
It is stored down to full precision of 100-nanosecond Ticks.
Whilst you can specify a DateTimeOffset
using a timezone, we store the UTC value. This is an unambiguous representation which will mean the same instant if read with other languages. However, because we lose the timezone specifier, this may cause confusion if you compare values using Ticks
instead of UtcTicks
.
Counters
Realm offers RealmInteger<T> as a special integer type. RealmInteger<T>
exposes an additional API that can more clearly express intent and generate better conflict resolution steps when using Synchronized Realms. The type argument, T
, can be either byte
, short
, int
, or long
.
Traditionally, a counter would be implemented by reading a value, incrementing it and setting it (myObject.Counter += 1
). This will not work well in an asynchronous situation—for example when two clients are offline—because both parties will read a value, say 10
, increment it, and store the value as 11
. Eventually, when they regain connectivity and try to merge their changes, they’ll agree that the counter is at 11
rather than the expected 12
.
RealmInteger
s are backed by traditional integer types, so no migration is required when changing a property type from T
to RealmInteger<T>
.
public class MyObject : RealmObject
{
[PrimaryKey]
public int Id { get; set; }
public RealmInteger<int> Counter { get; set; }
}
To increment the counter, simply call Increment:
var obj = realm.Find<MyObject>(1);
obj.Counter; // 0
obj.Counter.Increment(); // 1
obj.Counter.Decrement(); // 0
obj.Counter.Increment(2); // 2
obj.Counter.Increment(-5); // -3
To reset the counter, just assign it a new value. Keep in mind that in this case, regular merge rules apply and care must be taken to ensure proper semantics in your app:
var obj = realm.Find<MyObject>(1);
obj.Counter = 0; // Resetting to 0
Because RealmInteger<T>
is implicitly convertible to and from T
, you can use it without casting or without manually constructing it:
var obj = new MyObject
{
Counter = 3
};
realm.Write(() => realm.Add(obj));
DoSomething(obj.Counter);
public void DoSomething(int value)
{
// ...
}
Relationships
RealmObjects can be linked to each other by using RealmObject and IList properties.
For lists, you declare classes with an IList
property and an implementing object will be created when the getter is first used. You should only specify a get;
automatic method as there is no way to set such a list.
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
{
public string Name { get; set; }
}
You can use this property like you would any other:
realm.Write(() =>
{
var jim = realm.Add(new Person { Name = "Jim" });
var rex = realm.Add(new Dog { 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 IList
properties. When you use these properties, you get back a collection which may be empty or contains related RealmObjects of a single type.
To add a “Dogs” property on our Person
model that links to multiple dogs, we simply declare it as an IList<Dog>
property. You don’t have to initialize the IList
– the Realm SDK takes care of that for you. Just add and delete Dog
objects from the list to establish the relationship between a given Person
and a Dog
.
public class Dog : RealmObject
{
public string Name { get; set; }
}
public class Person : RealmObject
{
// ... other property declarations
public IList<Dog> Dogs { get; }
}
jim.Dogs.Add(rex);
jim.Dogs.Count(); // => 1 -- nobody but rex
Inverse Relationships
Links are unidirectional. So if a to-many property Person.Dogs
links to a Dog
instance and a to-one property Dog.Owner
links to Person
, these links are independent from one another. Appending a Dog
to a Person
instance’s Dogs
property doesn’t automatically set the dog’s Owner
property to this Person
. Because manually synchronizing pairs of relationships is error prone, complex and duplicates information, Realm provides backlink properties to represent these inverse relationships.
With backlink properties, you can obtain all objects that link to a given object from a specific property. For example, a Dog
object can have a property named Owners
that contains all of the Person
objects that have this exact Dog
object in their Dogs
property. This is done by making the Owners
property of type IQueryable<Person>
and then applying the [Backlink] attribute to indicate the relationship that Owners
has with the Person
model objects.
public class Dog : RealmObject
{
[Backlink(nameof(Person.Dogs))]
public IQueryable<Person> Owners { get; }
}
A backlink property can point either to an IList<RealmObject>
property (to-many relationship) or a RealmObject property (to-one relationship):
public class Ship : RealmObject
{
public Captain Captain { get; set; }
}
public class Captain : RealmObject
{
[Backlink(nameof(Ship.Captain))]
public IQueryable<Ship> Ships { get; }
}
Optional Properties
Reference types such as string
, byte[]
and related RealmObject values can be null.
Nullable value types such as int?
are fully supported.
We also support the optional DateTimeOffset?
to complete the full range of having an optional version of each property type.
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;
}
}
Indexed Properties
Currently only strings, integers, booleans and DateTimeOffset
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 IList<Dog> Dogs { get; }
}
Mapped Properties
Realm files are inherently platform agnostic and can be opened or synchronized with any of the available bindings. To do that, the schema should be identical for different languages, including casing of property names. To avoid writing non-standard C# classes you can use the [MapTo] attribute, e.g.:
// C# model
public class Person : RealmObject
{
[MapTo("name")]
public string Name { get; set; }
[MapTo("dogs")]
public IList<Dog> Dogs { get; }
}
// Equivalent js model
class Person {}
Person.schema = {
name: 'Person',
properties: {
name: {type: 'string'},
dogs: {type: 'list', objectType: 'Dog'}
}
};
Auto-Updating Objects
As soon as an instance of a RealmObject is managed, it becomes a live, auto-updating views into the underlying data, which means objects never have to be refreshed. Modifying the properties of an object will immediately reflect in any other instances referring to the same object.
var myDog = new Dog { Name = "Fido", Age = 1 };
realm.Write(() =>
{
realm.Add(myDog);
});
var myPuppy = realm.All<Dog>().First(d => d.Age == 1);
realm.Write(() =>
{
myPuppy.Age = 2;
}
myDog.Age; // => 2
This aspect of RealmObject not only keeps Realm fast and efficient, it allows your code to be simpler and more reactive. For example, if your UI code is dependent on a specific Realm object, you don’t need to worry about refreshing or re-fetching it before triggering a UI redraw.
You can subscribe to Realm notifications to know when Realm data in an object is updated, indicating when your app’s UI should be refreshed.
PrimaryKey Properties
The [PrimaryKey] attribute can be specified on one property in a RealmObject class. Declaring a PrimaryKey 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 PrimaryKeys. There is no particular storage or performance advantage to using char
or smaller integer types. They are supported only in case you already have a property of that type.
Putting the [PrimaryKey] attribute on multiple properties will compile but is validated at runtime and will throw an exception reporting that Schema validation failed, as soon as you try to open that Realm.
Once an object with a PrimaryKey is added to a Realm, the PrimaryKey cannot be changed.
Trying to create another object with the same key will throw a RealmDuplicatePrimaryKeyValueException.
You can retrieve an object by PrimaryKey very quickly using Realm.Find which performs more streamlined query construction than using LINQ, as well as using an index. It is overloaded to take string, character or integer keys, eg:
public class Person : RealmObject
{
[PrimaryKey]
public string SSN { get; set; }
public string Name { get; set; }
public IList<Dog> Dogs { get; }
}
var objByPK = realm.Find<Person>("457-55-5462");
Model Inheritance
Realm .NET does not allow models to be further subclassed in any way. The weaver will fail if it detects a class that inherits indirectly from the RealmObject class.
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, i.e. if inserting multiple items in a loop, prefer to do it in a single transaction, rather than creating a transaction for each item, e.g.:
realm.Write(() =>
{
var people = Realm.All<Person>();
foreach (var person in people)
{
person.Age += 1;
}
});
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 by default unless an exception was thrown.
Also see the Realm.WriteAsync() discussion below under Threading.
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 can turn an instance of this class into a persisted, live object using the Realm.Add() method:
realm.Write(() =>
{
var myDog = new Dog();
myDog.Name = "Rex";
myDog.Age = 10;
realm.Add(myDog);
});
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. We recommend you offload 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();
}
For objects where you have specified a [PrimaryKey], you can pass in update: true
in realm.Add to add the passed in object or update the existing one:
public class Person : RealmObject
{
[PrimaryKey]
public int Id { get; set; }
// ... other property declarations
}
realm.Write(() =>
{
realm.Add(new Person
{
Id = 1,
Name = "Kristian"
});
});
var kristianWithC = new Person
{
Id = 1,
Name = "Christian"
};
realm.Write(() => realm.Add(kristianWithC, update: true));
If you specify update: true
and pass in a class that does not have a PrimaryKey
, the method will not be able to find object to update, so it will behave as if update: false
was passed. If your object has relationships with other RealmObjects, they will be added or updated when they have PrimaryKey
specified, or simply added, when they don’t:
var kristian = new Person { Id = 1, Name = "Kristian" };
var rex = new Dog { Id = 1, Name = "Rex" };
kristian.Dogs.Add(rex);
realm.Write(() => realm.Add(kristian));
var christian = new Person { Id = 1, Name = "Christian" };
christian.Dogs.Add(new Dog { Id = 1, Name = "Bethoven" });
realm.Write(() => realm.Add(christian, update: true));
var newName = kristian.Name; // Christian
var newDogName = kristian.Dogs.First().Name; // Bethoven
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 realm.All<T> method and then apply Where
or other LINQ operators to get a filtered collection.
See the exact scope of LINQ implemented on our LINQ Support page.
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 collection conforms to the IQueryable interface so you can further iterate or process it:
foreach (var d in oldDogs)
{
Debug.WriteLine(d.Name);
}
String-Based Filter Support
You can perform more advanced querying by passing the query as a string to the Filter
method. For example:
realm.All<Child>().Filter("Parents.FirstName BEGINSWITH 'J'");
realm.All<Child>().Filter("Parents.@avg.Age > 50");
String-based filters also support Subqueries, Sorting, and Distinct queries:
realm.All<Person>().Filter("SUBQUERY(Dogs, $dog, $dog.Vaccinated == false).@count > 3");
realm.All<Dog>().Filter("TRUEPREDICATE SORT(Owner.FirstName ASC, Age DESC)");
realm.All<Dog>().Filter("TRUEPREDICATE DISTINCT(Age) SORT(Name)");
For more details on how to perform these complex string-based queries, see the NSPredicate Cheatsheet.
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 a class that implements IQueryable
and can be used 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 of references to the matching objects, and you work directly with the original objects that match your query.
Instead of retrieving them all into a list, you could also iterate through the query results, with the standard C# foreach
statement:
foreach (var person in johnsAndPeters) // iterate query
{
Debug.WriteLine(person.Name);
}
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<T>(). It returns an IQueryable
collection of all instances of the model class being queried - Person
in the example above.
You can then choose to apply a LINQ query further to restrict the set. Remember, until you start iterating the collection, there’s no overhead.
var ap = realm.All<Person>(); // this is all items 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
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 the Queryable
returned from Realm.All or subsequent LINQ clauses.
They work via the internal query engine to provide efficient sorted access without loading all objects in the results.
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();
var sortedPeople = realm.All<Person>()
.OrderBy(p => p.LastName)
.ThenBy(p => p.FirstName);
Chaining Queries
Since results are never copied, but are computed on request, you can very efficiently chain queries to gradually filter your data:
var teenagers = realm.All<Person>().Where(p => p.Age >= 13 && p.Age <= 20);
var firstJohn = teenagers.Where(p => p.FirstName == "John").First();
Limiting Results
Most other database technologies provide the ability to paginate results from queries (such as the LIMIT
keyword in SQLite). This is often done out of necessity to avoid reading too much from disk, or pulling too many results into memory at once.
Since queries in Realm are lazy, performing this sort of paginating behavior isn’t necessary at all, as Realm will only load objects from the results of the query once they are explicitly accessed.
If for UI-related or other implementation reasons you require a specific subset of objects from a query, it’s as simple as taking the IQueryable
object, and reading out only the objects you need.
While you may be tempted to use the LINQ Take, that is not yet supported. When added, it will enumerate and instantiate all the objects you request in one operation.
Realms
A Realm is an instance of a Realm Database container. Realms can be local or synchronized. A synchronized Realm uses the Realm Object Server to transparently synchronize its contents with other devices. While your application continues working with a synchronized Realm as if it’s a local file, the data in that Realm might be updated by any device with write access to that Realm. In practice, your application works with any kind of Realm the same way, although opening a synchronized Realm requires a User that’s been authenticated to the Object Server and that’s authorized to open that Realm.
For a more detailed discussion about Realms, read The Realm Data Model.
Opening Realms
Opening a Realm is simply performed by instantiating a new Realm
object. We’ve seen this used already in examples:
// Get a Realm instance
var realm = Realm.GetInstance();
With no arguments, GetInstance
instantiates the Default Realm. However, you can also specify a RealmConfiguration
object.
Configuring a Realm
A RealmConfiguration allows you to control the database path in several ways. (Note that you can’t change the path once the configuration has been constructed.) You can:
- Change the entire path by passing in a new absolute path.
- Put your Realm files in a subdirectory of the standard location by passing in a relative path.
- Change the Realm filename by just passing in a new filename.
var config = new RealmConfiguration("OtherPath.realm");
var otherRealm = Realm.GetInstance(config);
In the configuration you can also specify the schema version. For details about this, see the Migrations section. While developing, you can also set the property ShouldDeleteIfMigrationNeeded to true
. This will cause Realm.GetInstance() to delete the existing database in case your schema mismatches that in the file being opened. This makes it easier to play around with models before releasing. However, do not release an app with this flag. You might want to set it within an #if DEBUG
section to avoid accidents.
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.
It is important to note that Realm instances are per-configuration, per-thread singletons. This means that Realm.GetInstance() will return the same instance every time it’s called on the same thread for the same configuration.
The Default Realm
Calling Realm.GetInstance() without passing any arguments returns the “default Realm” instance. The default Realm simply maps to a file called default.realm
located in Environment.SpecialFolder.Personal
.
In-memory Realms
With an InMemoryConfiguration
configuration, you can create a Realm that runs entirely in memory without being persisted to disk:
var config = new InMemoryConfiguration("some-identifier");
var realm = Realm.GetInstance(config);
In-memory Realms might still use disk space if memory is running low, but all files created by an in-memory Realm will be deleted when the Realm is closed. Creating an in-memory Realm with the same identifier as a persisted Realm isn’t allowed - identifiers still have to be unique.
When all in-memory Realm instances with a particular identifier are disposed or garbage collected, that frees all that Realm’s data. To keep an in-memory Realm “alive” throughout your app’s execution, hold onto a reference to it.
Asynchronously Opening Realms
If opening a Realm might require a time-consuming operation, such as applying migrations, compaction or downloading the remote contents of a synchronized Realm, you should use the Realm.GetInstanceAsync API to perform all work needed to get the Realm to a usable state on a background thread before completing the returned Task<Realm>
. You should also use Realm.GetInstanceAsync
with Realms that the user has read permissions only.
For example:
var config = new RealmConfiguration
{
SchemaVersion = 1,
MigrationBlock = (migration, oldSchemaVersion) =>
{
// potentially lengthy data migration
}
};
try
{
var realm = await Realm.GetInstanceAsync(config);
// Realm successfully opened, with migration applied on background thread
}
catch (Exception ex)
{
// Handle exception that occurred while opening the Realm
}
Closing Realm Instances
The Realm class 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.
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.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.
Bundling a Realm with an App
If you want to bundle a Realm with your app, see this StackOverflow answer which explains how to include files in Xamarin projects as resources and copy them for use.
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[] { typeof(LoneClass) };
// or specifying two classes in the Realm
config.ObjectClasses = new[] { typeof(Dog), typeof(Cat) };
Compacting Realms
Over the course of using your application, the memory used by Realm might become fragmented and take up more space than necessary. To rearrange the internal storage and potentially reduce the filesize, you can compact the Realm file by specifying a RealmConfiguration.ShouldCompactOnLaunch callback when opening a Realm file:
var config = new RealmConfiguration
{
ShouldCompactOnLaunch = (totalBytes, usedBytes) =>
{
// totalBytes refers to the size of the file on disk in bytes (data + free space)
// usedBytes refers to the number of bytes used by data in the file
// Compact if the file is over 100MB in size and less than 50% 'used'
var oneHundredMB = 100 * 1024 * 1024;
return totalBytes > oneHundredMB && (double)usedbytes / totalBytes < 0.5;
}
};
try
{
var realm = Realm.GetInstance(config);
}
catch (Exception e)
{
// handle error compacting or opening Realm
}
The Realm will be compacted if you return true
from the ShouldCompactOnLaunch
delegate and the Realm file is not currently in use.
Alternatively, you can compact the file without obtaining a Realm instance by calling Realm.Compact(config):
var config = new RealmConfiguration("my.realm");
Realm.Compact(config);
Notes on compacting
- There should not be any open realm instances that use the file.
- The file system should have free space for at least a copy of the Realm file.
- If any operation fails, the Realm file is left untouched.
- The method will return
true
if the operation is successful andfalse
if there are open Realm instances accessing the same file. - At the moment, we don’t provide API for estimating the potential size gain by compacting a Realm. The recommended approach is to monitor your database’s size between launches and compact it if it crosses some threshold.
Deleting Realm Files
In some cases, such as clearing caches, or resetting your entire dataset, it may be appropriate to completely delete a Realm file from disk.
Unlike most files, Realm files are memory-mapped and Realm instances expect the files to be available for the duration of the instance’s lifetime.
To avoid your application code having to be aware of all files in a Realm, we provide a convenience method Realm.DeleteRealm(RealmConfiguration).
var config = new RealmConfiguration("FileWeThrowAway.realm");
Realm.DeleteRealm(config);
var freshRealm = Realm.GetInstance(config);
Freezing Realms and Files
You can “pin” objects, collections, or Realms at a particular version. A frozen object is immutable and will not be updated when changes occur on the database, and any attempt to write to – or subscribe to changes on – a frozen object throws an exception.
To create a frozen realm, call the Freeze()
method on a realm:
var liveRealm = Realm.GetInstance();
// Get a frozen Realm from a live one
var frozenRealm = liveRealm.Freeze();
You query frozen realms like normal realms:
var tasks = frozenRealm.All<Task>().Where(t => t.Priority > 5);
The following code shows how to generate a collection from a live realm and then freeze it:
var frozenTasks = liveRealm.All<Task>().Where(t => t.Priority > 5).Freeze();
frozenTasks.IsFrozen(); // true
// Objects in a frozen query are frozen themselves
frozenTasks().First().IsFrozen; // true
The following code shows how to freeze a single object:
var liveTask = liveRealm.Find<Task>("123");
var frozenTask = liveTask.Freeze();
Finally, you can freeze a live object “in place” if you no longer need the live data:
liveTask.FreezeInPlace();
liveTYask.IsFrozen; // true
NOTE: Having multiple frozen Realms at different versions will increase file sizes. To control the maximum number of pinned versions, use the MaxNumberOfActiveVersions
property on your RealmConfiguration
class. When set, Realm will keep track of the number of active versions and throw an exception if the limit is exceeded.
Threading
Within individual threads, you can just treat everything as regular objects without worrying about concurrency or multithreading. There is no need for any locks or resource coordination to access them (even if they are simultaneously being modified on other threads) and it is only modifying operations that have to be wrapped in transactions.
Realm makes concurrent usage easy by ensuring that each thread always has a consistent view of the Realm. You can have any number of threads working on the same Realms in parallel, and since they all have their own snapshots, they will never cause each other to see inconsistent state.
The only thing you have to be aware of is that you cannot have multiple threads sharing the same instances of Realm objects. If multiple threads need to access the same objects, they will each need to get their own instances (otherwise changes happening on one thread could cause other threads to see incomplete or inconsistent data).
Seeing Changes From Other Threads
On the main UI thread (or any thread with a runloop/looper), objects will automatically update with changes from other threads between each iteration of the runloop. At any other time you will be working on the snapshot, so individual methods always see a consistent view and never have to worry about what happens on other threads.
When you initially open a Realm on a thread, its state will be based off the most recent successful write commit, and it will remain on that version until refreshed. Realms are automatically refreshed at the start of every runloop iteration. If a thread has no runloop (which is generally the case in a background thread), then Realm.Refresh() must be called manually in order to advance the transaction to the most recent state.
Realms are also refreshed when write transactions are committed with Transaction.Commit().
Failing to refresh Realms on a regular basis could lead to some transaction versions becoming “pinned”, preventing Realm from reusing the disk space used by that version, leading to larger file sizes. Refer to our Current Limitations for more details on this effect.
Passing Instances Across Threads
Persisted instances of Realm, RealmObject, IQueryable
returned from Realm.All, or IList
properties of RealmObjects can only be used on the thread on which they were created, otherwise an exception is thrown. This is one way Realm enforces transaction version isolation. Otherwise, it would be impossible to determine what should be done when an object is passed between threads at different transaction versions without a potentially extensive relationship graph.
Realm exposes a mechanism to safely pass thread-confined instances in three steps:
- Obtain a ThreadSafeReference by calling one of the ThreadSafeReference.Create overloads with the thread-confined object.
- Pass that ThreadSafeReference to a destination thread or queue.
- Resolve this reference on the target Realm by calling one of the Realm.ResolveReference overloads. Use the returned object as you normally would.
For example:
var person = new Person { Name = "Jane" };
realm.Write(() => realm.Add(person));
var personReference = ThreadSafeReference.Create(person);
Task.Run(() =>
{
var otherRealm = Realm.GetInstance(realm.Config);
var otherPerson = otherRealm.ResolveReference(personReference);
if (otherPerson == null)
{
return; // person was deleted
}
realm.Write(() =>
{
otherPerson.Name = "Jane Doe";
});
});
A ThreadSafeReference object must be resolved at most once. Failing to resolve it will result in the source version of the Realm being pinned until the reference is deallocated. For this reason, ThreadSafeReference should be short-lived.
ThreadSafeReference wrappers are provided for RealmObject
, IList<RealmObject>
, and IQueryable<RealmObject
. They should only be used for persisted instances only, as the standalone versions of those are intrinsically not thread confined.
Using a Realm Across Threads
To access the same Realm file from different threads, you must initialize a new Realm to get a different instance for every thread of your app. As long as you specify the same configuration, all Realm instances will map to the same file on disk.
Sharing Realm instances across threads is not supported. Realm instances accessing the same realm file must also all use the same RealmConfiguration.
Asynchronous Writes
The Realm.WriteAsync() method is a special form of Write
which makes it easy to offload the UI thread by performing writes on a background thread.
Caution: WriteAsync opens a temporary Realm instance, which is passed to your lambda as a parameter, seen in the example below. Be careful to only use the passed-in instance in the lambda and not the original thread’s realm. Due to the way lambdas capture the calling context, failing to change this code will not get a compiler error but instead a runtime exception will be thrown.
await realm.WriteAsync(tempRealm =>
{
var pongo = tempRealm.All<Dog>().Single(d => d.Name == "Pongo");
var missis = tempRealm.All<Dog>().Single(d => d.Name == "Missis");
for (var i = 0; i < 15; i++)
{
tempRealm.Add(new Dog
{
Breed = "Dalmatian",
Mum = missis,
Dad = pongo
});
}
});
When its threaded action is complete, WriteAsync also calls Realm.Refresh() to advance the read state of the original (usually UI) thread. That thread will immediately receive notifications of any changes in the background threads.
There is no point using WriteAsync from code which you know is already running on a worker thread as it will just forward a call to Write without creating an extra thread. However, it is quite safe to use from any thread and adds no meaningful overhead. It is important to consider that on a background thread, WriteAsync will operate on the current Realm instance, rather than create a new instance.
In general, since realm writes are very fast, we recommend using Write for interface-driven changes (e.g. the user types some string, or checks a checkbox) where only a few values change at a time. WriteAsync is useful in scenarios where there are hundreds or thousands of changes (e.g. fetching objects from the network or batch updating multiple objects) and performing them synchronously might lead to interface jitters.
Notifications
On Android, change listeners only work on Looper threads. For non-looper threads, you manually have to call Realm.Refresh() or install a SynchronizationContext on the thread before opening the Realm. A 3rd party library you could use to simplify that is Nito.AsyncEx.Context.
Realm Notifications
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 (also by another thread or process):
realm.RealmChanged += (s, e) =>
{
// Update UI
}
Keep in mind that the current implementation of RealmChanged does not provide detailed information for the changes, so if you need that, check the Object Notifications or Collection Notifications sections.
Object Notifications
RealmObject implements INotifyPropertyChanged so if you want to listen for changes, you can subscribe to the PropertyChangedEvent event.
public class Account : RealmObject
{
public long Balance { get; set; }
}
If you subscribe to the PropertyChangedEvent event on your class, your handler will be triggered when the Balance
property is changed.
var account = new Account();
realm.Write(() => realm.Add(account));
account.PropertyChanged += (sender, e) =>
{
Debug.WriteLine($"New value set for {e.PropertyName}");
}
realm.Write(() => account.Balance = 10); // => "New value set for Balance"
Note that it doesn’t matter whether you subscribe before or after adding the object to the Realm. The event will be raised as expected in either case.
This is handy in and of itself, but furthermore, it enables databinding with Xamarin.Forms. For more info, see From Data Bindings to MVVM in the Xamarin Docs.
Collection Notifications
IQueryable
objects returned from Realm.All<T>() or subsequent LINQ clauses are live queries, meaning that they are always kept up to date. Any time you iterate over the collection, you will get a result that is up to date with the latest changes. Similarly IList
properties on RealmObjects represent live to-many relationships, meaning that each time you iterate over them, you will get the most up-to-date collection of related objects.
The runtime objects backing both of these collections also implement IRealmCollection<T> which means that it exposes two mechanisms for subscribing for change notifications. We’ve provided a convenience extension method: AsRealmCollection that will cast an IList<T>
or IQueryable<T>
to IRealmCollection<T>
.
If you prefer to use the standard .NET INotifyCollectionChanged interface that works nicely with MVVM and data-binding, you can pass the IRealmCollection<T> directly to your views.
Alternatively, if you wish more detailed change information, that is useful for manipulating a UITableView
or ListView
directly (e.g. in a Xamarin Native UI project), you can use the SubscribeForNotifications extension method. You give this method a delegate that will be called with a change set, telling you what has been added, removed or modified.
var token = realm.All<Person>().SubscribeForNotifications ((sender, changes, error) =>
{
// Access changes.InsertedIndices, changes.DeletedIndices, and changes.ModifiedIndices
});
// Later, when you no longer wish to receive notifications
token.Dispose();
Migrations
When working with any database, it is likely your data model will change over time. Since data models in Realm are defined as standard C# classes, making model changes is as easy as changing any other class. For example, suppose we have the following Person
model:
public class Person : RealmObject
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
We want to update the data model to require a FullName
property, rather than separate first and last names. To do this, we simply change the object interface to the following:
public class Person : RealmObject
{
public string FullName { get; set; }
public int Age { get; set; }
}
At this point if you had saved any data with the previous model version, there will be a mismatch between what Realm sees defined in code and the data Realm sees on disk. When this occurs, an exception will be thrown when you try to open the existing file unless you run a migration.
Performing a Migration
The minimal solution you can perform is to bump the schema version. When unspecified, Realms will have a schema version set to 0. As soon as you make a schema change, you must increase the schema version in your configuration. This way, we avoid accidental schema changes that potentially destroy data.
var config = new RealmConfiguration() { SchemaVersion = 1 };
var realm = Realm.GetInstance(config);
Now, if you do nothing more than this and run it with a database file that was created with the old schema, all the FirstName
and LastName
entries will be deleted and every Person
will have an empty FullName
property. This probably isn’t what you wanted here. So you must tell Realm how to handle the migration.
We do this by specifying a MigrationCallback function. This function will receive a Migration object, which will contain two Realm properties: OldRealm and NewRealm. You can now copy and adapt data from the old schema to the new.
Note that if you simply rename a class or a property, Realm will not be able to detect that and you will have to copy data in the migration as well.
This callback function will be called exactly once when a Realm with a lower version is opened. You should migrate all updated classes during that call.
As our Person
model no longer contains the FirstName
and LastName
properties, we cannot access those through the typed API. However, we can utilize the dynamic API and achieve the same thing.
var config = new RealmConfiguration
{
SchemaVersion = 1,
MigrationCallback = (migration, oldSchemaVersion) =>
{
var newPeople = migration.NewRealm.All<Person>();
// Use the dynamic api for oldPeople so we can access
// .FirstName and .LastName even though they no longer
// exist in the class definition.
var oldPeople = migration.OldRealm.All("Person");
for (var i = 0; i < newPeople.Count(); i++)
{
var oldPerson = oldPeople.ElementAt(i);
var newPerson = newPeople.ElementAt(i);
newPerson.FullName = oldPerson.FirstName + " " + oldPerson.LastName;
}
}
};
var realm = Realm.GetInstance(config);
As your app grows older and you have multiple changes to the model in different versions, you make a sequence of migrations by inspecting the oldSchemaVersion
parameter to your callback. Let’s say you change the Person
model further by changing the Age
field to a Birthday
field:
public class Person : RealmObject
{
public string FullName { get; set; }
public int Age { get; set; }
public DateTimeOFfset Birthday { get; set; }
}
This is then version 2 of the schema, and your migration setup could now look something like the following:
var config = new RealmConfiguration
{
SchemaVersion = 2,
MigrationCallback = (migration, oldSchemaVersion) =>
{
var newPeople = migration.NewRealm.All<Person>();
var oldPeople = migration.OldRealm.All("Person");
for (var i = 0; i < newPeople.Count(); i++)
{
var oldPerson = oldPeople.ElementAt(i);
var newPerson = newPeople.ElementAt(i);
// Migrate Person from version 0 to 1: replace FirstName and LastName with FullName
if (oldSchemaVersion < 1)
{
newPerson.FullName = oldPerson.FirstName + " " + oldPerson.LastName;
}
// Migrate Person from version 1 to 2: replace Age with Birthday
if (oldSchemaVersion < 2)
{
newPerson.Birthday = DateTimeOffset.Now.AddYears(-(int)oldPerson.Age);
}
}
}
};
var realm = Realm.GetInstance(config);
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.
Realm supports encrypting the database file on disk with AES-256+SHA2 by supplying a 64-byte encryption key when creating a Realm.
var config = new RealmConfiguration("Mine.realm");
config.EncryptionKey = new byte[64] // key MUST be exactly this size
{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78
};
var realm = Realm.GetInstance(config); // will create/open encrypted realm "Mine.realm"
This makes it so that all of the data stored on disk is transparently encrypted and decrypted with AES-256 as needed, and verified with a SHA-2 HMAC. The same encryption key must be supplied every time you obtain a Realm instance.
You should use a key that is unique to your app, not the one in the example above. Furthermore, if you need unique keys for each user, look into the Xamarin.Auth API.
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.
There is a small performance hit (typically less than 10% slower) when using encrypted Realms.
Sync
Are you looking to use Realm Mobile Platform to synchronize all of your Realm Databases? All sync related documentation has been moved to our platform documentation
Examples
You can find a number of examples in our repository in the examples folder.
Current Limitations
Realm is generally trying to have as few constraints as possible and we are continuously adding new features based on feedback from the community. However, Realm still has a few limitations. These have been compiled below.
Please refer to our GitHub issues for a more comprehensive list of known issues.
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 57 bytes in length. UTF-8 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. UTF-8 characters are supported. An exception will be thrown at your app’s initialization if this limit is exceeded.
- 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.
Threads
Although Realm files can be accessed by multiple threads concurrently, you cannot hand over Realms, Realm objects, queries, and results between threads. Read more about Realm’s threading.
File size & tracking of intermediate versions
You should expect a Realm database to take less space on disk than an equivalent SQLite database. If your Realm file is much larger than you expect, it may be because you have a RealmObject that is referring to an older version of the data in the database.
In order to give you a consistent view of your data, Realm only updates the active version accessed at the start of a run loop iteration. This means that if you read some data from the Realm and then block the thread on a long-running operation while writing to the Realm on other threads, the version is never updated and Realm has to hold on to intermediate versions of the data which you may not actually need, resulting in the file size growing with each write. The extra space will eventually be reused by future writes.
If you prefer to reclaim the space deterministically, refer to Compacting the Realm.
FAQ
Is Realm open source?
Yes! Realm’s internal C++ storage engine and the language SDKs over it are entirely open source and licensed under Apache 2.0. Realm also optionally includes a closed-source Realm Platform Extensions component, but that is not required to use Realm as an embedded database.
I see a network call to Mixpanel when I build 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.
Upgrading from Realm 3.x
Realm 4.x streamlines the architecture of the Realm package so it makes some dependencies obsolete. To update and clean up your project, follow these steps:
- Update the
Realm
package to the latest version. - Remove the
Realm.Database
,Realm.Server
, andRealm.DataBinding
packages. - Delete
$(SolutionDir)/Tools
folder - it previously contained a copy of the Realm weaver that is no longer being used. - The name of the Realm weaver was changed to just
Realm
. Edit yourFodyWeavers.xml
files to change<RealmWeaver />
to just<Realm />
. - Review the Changelog for breaking changes that may affect your project and update your code accordingly.
Troubleshooting
Crash Reporting
We encourage you to use a crash reporter in your application. Many Realm operations could potentially fail at runtime (like any other disk IO), so collecting crash reports from your application will help identify areas where either you (or us) can improve error handling and fix crashing bugs.
Most commercial crash reporters have the option of collecting logs. We strongly encourage you to enable this feature. Realm logs metadata information (but no user data) when throwing exceptions and in irrecoverable situations, and these messages can help debug when things go wrong.
Reporting Realm Issues
If you’ve found an issue with Realm, please either file an issue on GitHub or email us at help@realm.io with as much information as possible for us to understand and reproduce your issue.
The following information is very useful to us:
- Goals.
- Expected results.
- Actual results.
- Steps to reproduce.
- Code sample that highlights the issue (full projects that we can compile ourselves are ideal).
- Version of Realm, macOS or Windows & Visual Studio.
- Xcode if targeting iOS, NDK if targeting Android.
- Platform, OS version & architecture on which the bug happens (e.g., 64-bit iOS 8.1).
- Crash logs & stack traces. See Crash Reporting above for details.
Getting an No properties in class exception
You may see a System.InvalidOperationException
with the message “No properties in class, has linker stripped it?”.
There are three known causes:
- You either have no woven RealmObjects, probably because something went wrong with Fody, or
- Your RealmObjects had their properties stripped so appear to Realm to be empty.
- You are using some kind of code obfuscation tool which is interfering with model name detection (non-Xamarin platforms)
In the first case, the exception will be thrown by RealmSchema. See Failing to Weave for more details.
In the second case, the exception will be thrown from ObjectSchema. We have had some users run into problems when they have the Linker Behaviour set to Link All and they lack the [Preserve(AllMembers = true)]
attribute on the class declaration. The linker will only preserve members which are referenced explicitly in the code. This means that if you have a property that would be persisted but is not referenced anywhere, it might be removed causing the schema to mismatch that of the database.
In the third case, the exception is thrown as the obfuscation tool causes the class and property names to be undetectable by our library. We rely on these class names to generate schema. To solve this issue, set your code obfuscation tool to ignore your model classes so they are not obfuscated.
By default, Realm builds the schema to describe all of your RealmObject subclasses in all your assemblies. This happens lazily and so is not triggered until your first call to GetInstance(). It will only do this a maximum of once per run, caching the result in memory.
For a given assembly, if you only wish to store some of your classes, you can specify a subset with ObjectClasses and use that configuration in GetInstance(myConf). That avoids the schema building for all and so will also avoid exceptions.
Otherwise, if you have unused classes, add the Preserve
attribute.
Fody: An unhandled exception occurred
This common build failure can easily be triggered when you have already built a project and just add a new RealmObject subclass.
Choosing Build or Run will not rebuild the project sufficiently to invoke the Fody Weaver.
Simply choose Rebuild on your project and it should build without error.
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 Realm
.
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.
If this doesn’t work, there seems to be a problem with Fody and Microsoft.Bcl.Build.targets
. Removing the following line from your .csproj file might help:
<Import Project="..\..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
For more details about this, see this StackOverflow answer.
Troubleshooting WriteAsync Issues
Inside WriteAsync
, we check for a non-null SynchronizationContext.Current to indicate if you might be on the UI thread. This check may be confused by a well-established pattern of programming used where people also set Current
in their worker threads, so they can use the Post method to run code on the UI thread. This pattern dates back to a 2007 MSDN blog posting.
It is not a problem if you have set SynchronisationContext.Current
but it will cause WriteAsync
to dispatch again on the thread pool, which may create another worker thread. So, if you are using Current
in your threads, consider calling just Write
instead of WriteAsync
.
Realm Core Binary Fails to Download
When building Realm, part of the process includes downloading the core library as a static binary and integrating it into the Realm project. This is part of the wrappers
building by Makefile
.
It’s been reported that in certain instances, the core binary fails to download with the following error:
Downloading core failed. Please try again once you have an Internet connection.
This error can occur due to any of the following reasons:
- Your IP address range is from a region that is on the list of United States embargoes. In order to comply with U.S. law, Realm has not been made available in that region. For more information, please see our license.
- You are located in mainland China, and due to the country-wide firewall you are not able to properly access CloudFlare or Amazon AWS S3 services at the moment. Please see this issue on one of our other products for more information.
- Amazon AWS S3 could be experiencing service issues. Please check the AWS Service Health Dashboard and try again later.
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, as much logging output as possible, the Realm file, and a project that shows the issue.
- Have a feature request? Open an issue on our repo. Tell us what the feature should do, and why you want the feature.