This is not the current version. View the latest documentation
If you’re looking to use Realm from Objective‑C, or from mixed Objective‑C & Swift apps please see Realm Objective‑C instead. Using both Realm Objective‑C and Realm Swift simultaneously is not supported.
Realm Swift 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 Swift classes
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
}
class Person: Object {
dynamic var name = ""
dynamic var picture = NSData()
let dogs = List<Dog>()
}
// Use them like regular Swift objects
let mydog = Dog()
mydog.name = "Rex"
println("name of dog: \(mydog.name)")
// Persist your data easily
let realm = Realm()
realm.write {
realm.add(mydog)
}
// Query it from any thread
dispatch_async(dispatch_queue_create("background", 0)) {
realm.objects(Dog).filter("age > 8") // => Results<Dog>
}
If you have an app that is presently using Core Data and have been considering switching to Realm, we recently published an article discussing how to go about doing this. Go check it out!
Getting Started
Download Realm Swift or see the source on GitHub.
Prerequisites
- Apps using Realm can target: iOS 8 or later, OS X 10.9 or later & WatchKit. (iOS 7 cannot be supported because it does not allow using 3rd party dynamic frameworks, and it is impossible to build static libraries that contain Swift code.)
- Xcode 6.3 or later required.
- Current releases of Realm Swift target Swift 1.2. Using Swift 2.0 is possible, but support has to be enabled manually until Apple officially releases this new version of the language.
Installation (Swift 1.2)
- Download the latest release of Realm and extract the zip.
- Go to your Xcode project’s “General” settings. Drag
RealmSwift.framework
andRealm.framework
from theios/
orosx/
directory to the “Embedded Binaries” section. Make sure Copy items if needed is selected and click Finish. - In your unit test target’s “Build Settings”, add the parent path to
RealmSwift.framework
in the “Framework Search Paths” section. - If using Realm in an iOS project, create a new “Run Script Phase” in your app’s target’s “Build Phases” and paste the following snippet in the script text field:
bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework/strip-frameworks.sh"
This step is required to work around an App Store submission bug when archiving universal binaries.
- Install CocoaPods 0.37.1 or later.
- In your Podfile, add
use_frameworks!
andpod 'RealmSwift'
to your main and test targets. - From the command line, run
pod install
. - Use the
.xcworkspace
file generated by CocoaPods to work on your project!
- Install Carthage 0.7.5 or later.
- Add
github "realm/realm-cocoa"
to your Cartfile. - Run
carthage update
. - iOS: Drag
RealmSwift.framework
andRealm.framework
from theCarthage/Build/iOS/
directory to the “Linked Frameworks and Libraries” section of your Xcode project’s “General” settings.
OS X: DragRealmSwift.framework
andRealm.framework
from theCarthage/Build/Mac/
directory to the “Embedded Binaries” section of your Xcode project’s “General” settings. -
iOS: On your application targets’ “Build Phases” settings tab, click the “+” icon and choose “New Run Script Phase”. Create a Run Script with the following contents:
/usr/local/bin/carthage copy-frameworks
and add the paths to the frameworks you want to use under “Input Files”, e.g.:
$(SRCROOT)/Carthage/Build/iOS/Realm.framework
$(SRCROOT)/Carthage/Build/iOS/RealmSwift.frameworkThis script works around an App Store submission bug triggered by universal binaries.
Installation (Swift 2.0)
Realm is maintaining parallel support for Swift 2.0 until Apple officially releases it later this year.
- Clone Realm from our repository on GitHub.
- Run
REALM_SWIFT_VERSION=2.0 sh build.sh build
in the cloned repository. - Make sure that you remove the existing binaries of
RealmSwift.framework
andRealm.framework
from your project. - Go to your Xcode project’s “General” settings. Drag the versions of
RealmSwift.framework
andRealm.framework
frombuild/ios/swift-2.0/
orbuild/osx/swift-2.0/
directory to the “Embedded Binaries” section. Make sure Copy items if needed is selected and click Finish. - In your unit test target’s “Build Settings”, add the parent path to
RealmSwift.framework
in the “Framework Search Paths” section.
In your Podfile, add the following to your main and test targets:
# You need to add both “Realm” & “RealmSwift”
use_frameworks!
pod 'Realm', :git => 'https://github.com/realm/realm-cocoa.git', :branch => 'swift-2.0'
pod 'RealmSwift', :git => 'https://github.com/realm/realm-cocoa.git', :branch => 'swift-2.0'
Swift 2.0 is not currently available via Carthage, as Realm Swift relies on prebuilt binaries currently since Carthage doesn’t offer a way to specify the desired scheme to build.
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.
Xcode Plugin
Our Xcode plugin makes it easy to generate new Realm models.
The easiest way to install the Realm Xcode plugin is through Alcatraz under the name “RealmPlugin”. You can also install the plugin manually by opening plugin/RealmPlugin.xcodeproj
contained in the release zip and clicking build. You will need to quit and relaunch Xcode to see our plugin. If you use the Xcode menu to create a new file (File > New > File… — or ⌘N) you should see a new option to create a new Realm model.
API Reference
You can consult our full API reference for all classes, methods & more.
Examples
You can find example applications for both iOS and OS X in our release zip under examples/
, demonstrating how to use many features of Realm like migrations, how to use it with UITableViewControllers, encryption, command-line tools and much more.
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.
- Have a feature request? Open an issue on our repo. Tell us what the feature should do, and why you want the feature.
Models
Realm data models are defined using traditional Swift classes with properties. Simply subclass Object
or an existing model class to create your Realm data model objects. Realm model objects mostly function like any other Swift objects - you can add your own methods and protocols 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 you can’t access its ivars directly for any persisted properties.
Relationships and nested data structures are modeled simply by including properties of the target type or Lists for typed lists of objects.
import RealmSwift
// Dog model
class Dog: Object {
dynamic var name = ""
dynamic var owner: Person? // Can be optional
}
// Person model
class Person: Object {
dynamic var name = ""
dynamic var birthdate = NSDate(timeIntervalSince1970: 1)
let dogs = List<Dog>()
}
Since Realm parses all models defined in your code at launch, they must all be valid, even if they are never used.
See Object for more details.
Supported Types
Realm supports the following property types: Bool
, Int8
, Int16
, Int32
, Int64
, Double
, Float
, String
, NSDate
truncated to the second, and NSData
.
You can use List<Object>
and Object
to model relationships such as to-many and to-one. Subclassing Object
is also supported.
Relationships
Objects can be linked to each other by using Object and List properties. Lists have an interface very similar to Array and objects contained in a List can be accessed using indexed subscripting. Unlike Arrays, Lists are typed and only hold Objects of a single subclass type. For more details see List.
Assuming your Person model has already been defined (see above) let’s create another model called Dog:
class Dog: Object {
dynamic var name = ""
}
To-One Relationships
For many-to-one or one-to-one relationships, simply declare a property with the type of your Object subclass:
class Dog: Object {
... // other property declarations
dynamic var owner: Person?
}
You can use this property like you would any other:
let jim = Person()
let rex = Dog()
rex.owner = jim
When using Object 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 List properties. Lists contain other Objects of a single type and have an interface very similar to a mutable Array.
To add a “dogs” property on our Person model that links to multiple dogs, we can declare a property of type List<Dog>
:
class Person: Object {
... // other property declarations
let dogs = List<Dog>()
}
You can access and assign List properties as usual:
let someDogs = Realm().objects(Dog).filter("name contains 'Fido'")
jim.dogs.extend(someDogs)
jim.dogs.append(rex)
Inverse Relationships
With inverse relationships (also known as backlinks), you can obtain all objects linking to a given object through a specific property. For example, calling Object().linkingObjects(_:forProperty:)
on a Dog
instance will return all objects of the specified class linking to the calling instance with the specified property. It’s possible to simplify this pattern by defining a read-only (computed) owners
property on Dog
:
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
var owners: [Person] {
// Realm doesn't persist this property because it only has a getter defined
// Define "owners" as the inverse relationship to Person.dogs
return linkingObjects(Person.self, forProperty: "dogs")
}
}
Property Attributes
Realm model properties need the var dynamic
attribute in order for these properties to become accessors for the underlying database data.
There’s one exception to this. List
properties cannot be declared as dynamic because generic properties cannot be represented in the Objective‑C runtime, which is used for dynamic dispatch of dynamic
properties.
Indexed Properties
Override Object.indexedProperties()
to set which properties in your model should be indexed:
class Book: Object {
dynamic var price = 0
dynamic var title = ""
override static func indexedProperties() -> [String] {
return ["title"]
}
}
Primary Keys
Override Object.primaryKey()
to set the model’s primary key. Declaring a primary key allows objects to be looked up and updated efficiently and enforces uniqueness for each value. Once an object with a primary key is added to a Realm, the primary key cannot be changed.
class Person: Object {
dynamic var id = 0
dynamic var name = ""
override static func primaryKey() -> String? {
return "id"
}
}
Ignored Properties
Override Object.ignoredProperties()
to prevent Realm from persisting model properties. Realm won’t interfere with the regular operation of these properties: they’ll be backed by ivars and you can freely override their setters and getters.
class Person: Object {
dynamic var tmpID = 0
var name: String { // computed properties are automatically ignored
return "\(firstName) \(lastName)"
}
dynamic var firstName = ""
dynamic var lastName = ""
override static func ignoredProperties() -> [String] {
return ["tmpID"]
}
}
Writes
All changes to an object (addition, modification and deletion) must be done within a write transaction.
Realm objects can be instantiated and used as standalone just like regular Swift objects. 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.
Creating Objects
When you have defined a model you can instantiate your Object subclass and add the new instance to the Realm. Consider this simple model:
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
}
We can create new objects in several ways:
// (1) Create a Dog object and then set its properties
var myDog = Dog()
myDog.name = "Rex"
myDog.age = 10
// (2) Create a Dog object from a dictionary
let myOtherDog = Dog(value: ["name" : "Pluto", "age": 3])
// (3) Create a Dog object from an array
let myThirdDog = Dog(value: ["Fido", 5])
- The most obvious is to use alloc-init (in Objective‑C) or the designated initializer (in Swift) to create an object. Please note that all properties must be set before an object can be added to the Realm.
- Objects can also be created from dictionaries using appropriate keys and values.
- Finally Object subclasses can be instantiated using arrays. The values in the array have to be in the same order as the corresponding properties in the model.
Nested Objects
If an object has properties that are Objects or Lists, these can be set recursively using nested arrays and/or dictionaries. You simply replace each object with a dictionary or array representing its properties:
// Instead of using already existing dogs...
let aPerson = Person(value: ["Jane", 30, [aDog, anotherDog]])
// ...we can create them inline
let aPerson = Person(value: ["Jane", 30, [["Buster", 5], ["Buddy", @6]]])
This will work for any combination of nested arrays and dictionaries. Note that an List may only contain Object
s, not basic types such as String
.
Adding Objects
You can add an object to a Realm like so:
// Create a Person object
let author = Person()
author.name = "David Foster Wallace"
// Get the default Realm
let realm = Realm()
// You only need to do this once (per thread)
// Add to the Realm inside a transaction
realm.write {
realm.add(author)
}
After you have added the object to the Realm you can continue using it, and 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.
See Realm and Object for more details.
Updating Objects
Realm a few ways to update objects, all of which offer different tradeoffs depending on the situation. Choose which one is best for your situation:
Typed Updates
You can update any object by setting its properties within a write transaction.
// Update an object with a transaction
realm.write {
author.name = "Thomas Pynchon"
}
Updating Objects With Primary Keys
If you have a primary key on your model, you can update an object or insert a new one if it doesn’t exist yet using Realm().add(_:update:)
.
// Creating a book with the same primary key as a previously saved book
let cheeseBook = Book()
cheeseBook.title = "Cheese recipes"
cheeseBook.price = 9000
cheeseBook.id = 1
// Updating book with id = 1
realm.write {
realm.add(cheeseBook, update: true)
}
If a book with a primary key id of 1 was not in the database, this would create a new book instead.
You can also partially update objects with primary keys by passing the subset of values you wish to update, along with the primary key:
// Assuming a "Book" with a primary key of `1` already exists.
realm.write {
realm.create(Book.self, value: ["id": 1, "price": 9000.0], update: true)
// the book's `title` property will remain unchanged.
}
Key-Value Coding
Object, Result, and List all conform to key-value coding (KVC). This can be useful when you need to determine which property to update at runtime.
Applying KVC to a collection is a great way to update objects in bulk without the overhead of iterating over a collection while creating accessors for every item.
let persons = Realm().objects(Person)
Realm().write {
persons.first?.setValue(true, forKeyPath: "isFirst")
// set each person's planet property to "Earth"
persons.setValue("Earth", forKeyPath: "planet")
}
Deleting Objects
Pass the object to be deleted to the Realm().delete(_:)
method within a write transaction.
let cheeseBook = ... // Book stored in Realm
// Delete an object with a transaction
realm.write {
realm.delete(cheeseBook)
}
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.
// Delete all objects from the realm
realm.write {
realm.deleteAll()
}
Queries
Queries return an Results
instance, which contains a collection of Objects. Results have an interface very similar to Array and objects contained in a Results can be accessed using indexed subscripting. Unlike Arrays, Results are typed and only hold Objects of a single subclass type.
All queries (including queries and property access) are lazy in Realm. Data is only read when the properties are accessed.
Results to a query are not copies of your data: modifying the results of a query (within a write transaction) will modify the data on disk directly. Similarly, you can traverse your graph of relationships directly from the Objects contained in a Results.
Retrieving Objects by Type
The most basic method for retrieving objects from a Realm is Realm().objects(_:)
, which returns a Results of all Object instances of the subclass type being queried the default Realm.
// Query a Realm
let dogs = Realm(path: "pets.realm").objects(Dog)
Querying with Predicates
If you’re familiar with NSPredicate, then you already know how to query in Realm. Objects, Realm, List, and Results all provide methods that allow you to query for specific Object instances by simply passing in an NSPredicate instance, predicate string, or predicate format string just as you would when querying an NSArray.
For example, the following would extend our earlier example by calling Results().filter(_:...)
to retrieve all dogs with the color tan and names beginning with ‘B’ from the default Realm:
let realm = Realm()
// Query using a predicate string
var tanDogs = realm.objects(Dog).filter("color = 'tan' AND name BEGINSWITH 'B'")
// Query using an NSPredicate
let predicate = NSPredicate(format: "color = %@ AND name BEGINSWITH %@", "tan", "B")
tanDogs = realm.objects(Dog).filter(predicate)
See Apple’s Predicates Programming Guide for more information about building predicates and use our NSPredicate Cheatsheet. Realm supports many common predicates:
- The comparison operands can be property names or constants. At least one of the operands must be a property name.
- The comparison operators ==, <=, <, >=, >, !=, and BETWEEN are supported for int, long, long long, float, double, and NSDate property types. Such as age == 45
- Identity comparisons ==, !=, e.g.
Results<Employee>().filter("company == %@", company)
- The comparison operators == and != are supported for boolean properties.
- For NSString and NSData properties, we support the ==, !=, BEGINSWITH, CONTAINS, and ENDSWITH operators, such as name CONTAINS ‘Ja’
- Case insensitive comparisons for strings, such as name CONTAINS[c] ‘Ja’. Note that only characters “A-Z” and “a-z” will be ignored for case.
- Realm supports the following compound operators: “AND”, “OR”, and “NOT”. Such as name BEGINSWITH ‘J’ AND age >= 32
- The containment operand IN such as name IN {‘Lisa’, ‘Spike’, ‘Hachi’}
- Nil comparisons ==, !=, e.g.
Results<Company>().filter("ceo == nil")
. Note this only works for objects with relationships, for example here, ceo is a property on the Company model. - ANY comparisons, such as ANY student.age < 21
- Note, although there’s no support for the aggregate expression type, we do support the BETWEEN operator type using object values, e.g.
Results<Person>.filter("age BETWEEN %@", [42, 43]])
.
For more, see Results().filter(_:...)
.
Ordering Results
Results allows you to specify a sort criteria and order based on a single or multiple properties. For example, the following calls sorts the returned dogs from the example above alphabetically by name:
// Sort tan dogs with names starting with "B" by name
let sortedDogs = Realm().objects(Dog).filter("color = 'tan' AND name BEGINSWITH 'B'").sorted("name")
For more, see Results().filter(_:...)
and Results().sorted(_:ascending:)
.
Chaining Queries
One unique property of Realm’s query engine is the ability to chain queries with very little transactional overhead when compared to traditional databases that require a separate trip to the database server for each successive query.
For example, if we wanted a result set for just the tan colored dogs, and the tan colored dogs whose names also started with ‘B’, you might chain two queries like this:
let tanDogs = Realm().objects(Dog).filter("color = 'tan'")
let tanDogsWithBNames = tanDogs.filter("name BEGINSWITH 'B'")
Realms
The Default Realm
You may have noticed so far that we have often initialized access to our realm
variable by calling Realm()
. That method returns an RLMRealm object that maps to a file called “default.realm” under the Documents folder of your app.
You can also set a custom path for the default realm using Realm.defaultPath
. This is especially useful when testing your app, or when sharing realms between apps in an iOS 8 Shared Container.
Other Realms
It’s sometimes useful to have multiple Realms persisted at different locations. For example, if you need to bundle data with an application, you can open it as a read-only Realm. See Realm(path:)
and Realm(path:encryptionKey:error:)
for more info.
Please note that if a custom path is used to initialize a Realm, it must be in a location with write permissions. The most common location to store writable Realm files is the “Documents” directory on iOS and the “Application Support” directory on OS X. Please respect Apple’s iOS Data Storage Guidelines, which recommend that documents that can be regenerated be stored in the <Application_Home>/Library/Caches
directory.
In-Memory Realms
Normally Realms are persisted to disk, but you can also create ones which operate purely in memory by using the Realm(inMemoryIdentifier:)
initializer:
let realm = Realm(inMemoryIdentifier: "MyInMemoryRealm")
In-memory Realms do not save data across app launches, but all other features of Realm will work as expected, including querying, relationships and thread-safety. This is a useful option if you need flexible data access without the overhead of disk persistence.
In-memory Realms may sometimes write to disk in a temporary directory for internal reasons (e.g. to set up a named pipe for cross-process notifications, or to swap to disk).
Notice: When all in-memory Realm instances with a particular identifier go out of scope with no references, all data is freed for that Realm. It is recommended that you hold onto a strong reference to any created in-memory Realms for the duration of your app.
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 path, 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 readOnly
value (either all readwrite, or all readonly).
Realm can be very efficient when writing large amounts of data by batching together multiple writes within a single transaction. Transactions can also be performed in the background using Grand Central Dispatch to avoid blocking the main thread. RLMRealm objects are not thread safe and cannot be shared across threads, so you must get a Realm instance in each thread/dispatch_queue in which you want to read or write. Here’s an example of inserting a million objects in a background queue:
dispatch_async(queue) {
autoreleasepool {
// Get realm and table instances for this thread
let realm = Realm()
// Break up the writing blocks into smaller portions
// by starting a new transaction
for idx1 in 0..<1000 {
realm.beginWrite()
// Add row via dictionary. Property order is ignored.
for idx2 in 0..<1000 {
realm.create(Person.self, value: [
"name": "\(idx1)",
"birthdate": NSDate(timeIntervalSince1970: idx2)
])
}
// Commit the write transaction
// to make this data available to other threads
realm.commitWrite()
}
}
}
Copying Objects Between Realms
Copying Realm objects to other Realms is as simple as passing in the original object to Realm().create(_:value:update:)
. For example, Realm().create(MyObjectSubclass.self, value: originalObjectInstance)
.
Finding a Realm File
If you need help finding your app’s Realm file, check this StackOverflow answer for detailed instructions.
Bundling a Realm with an App
It’s common to seed an app with initial data, making it available to your users immediately on first launch. Here’s how to do this:
- First, populate the realm. You should use the same data model as your final, shipping app to create a realm and populate it with the data you wish to bundle with your app. Since realm files are cross-platform, you can use an OS X app (see our JSONImport example) or your iOS app running in the simulator.
- In the code where you’re generating this realm file, you should finish by making a compacted copy of the file (see
Realm().writeCopyToPath(_:encryptionKey:)
). This will reduce the Realm’s file size, making your final app lighter to download for your users. - Drag the new compacted copy of your realm file to your final app’s Xcode Project Navigator.
- Go to your app target’s build phases tab in Xcode and add the realm file to the “Copy Bundle Resources” build phase.
- At this point, your bundled realm file will be accessible to your app. You can find its path by using
NSBundle.mainBundle().pathForResource(_:ofType:)
. - You can either create a read-only realm by calling
Realm(path:readOnly:encryptionKey:error:)
. Or, if you’d like to create a writable realm file based on this initial data, you can copy the bundled file to your application’s Documents directory usingNSFileManager.defaultManager().copyItemAtPath(_:toPath:error:)
and then construct your new realm by usingRealm(path:)
.
You can refer to our migration sample app for an example of how to use a bundled realm file.
Notifications
Realm instances send out notifications to other instances on other threads every time a write transaction is committed. These notifications can be observed by registering a block:
// Observe Realm Notifications
let token = realm.addNotificationBlock { notification, realm in
viewController.updateUI()
}
The notification stays active as long as a reference is held to the returned notification token. You should hold onto a strong reference to this token on the class registering for updates, as notifications are automatically un-registered when the notification token is deallocated.
See Realm().addNotificationBlock(_:)
and Realm().removeNotification(_:)
for details.
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 Swift interfaces, making model changes is as easy as changing any other Swift interface. For example, suppose we have the following Person
model:
class Person: Object {
dynamic var firstName = ""
dynamic var lastName = ""
dynamic var age = 0
}
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:
class Person: Object {
dynamic var fullName = ""
dynamic var age = 0
}
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 unless you run a migration.
Performing a Migration
You define a migration and the associated schema version by calling setSchemaVersion(_:_:_:)
. Your migration block provides all the logic for converting data models from previous schemas to the new schema. After calling setSchemaVersion(_:_:_:)
, any Realms which require migration will automatically apply the migration block provided and be updated to the provided version.
For example, suppose we want to migrate the Person
model declared earlier. The minimal necessary migration block would be the following:
// Inside your application(application:didFinishLaunchingWithOptions:)
// Notice setSchemaVersion is set to 1, this is always set manually. It must be
// higher than the previous version (oldSchemaVersion) or an RLMException is thrown
setSchemaVersion(1, Realm.defaultPath, { migration, oldSchemaVersion in
// We haven’t migrated anything yet, so oldSchemaVersion == 0
if oldSchemaVersion < 1 {
// Nothing to do!
// Realm will automatically detect new properties and removed properties
// And will update the schema on disk automatically
}
})
// now that we have called `setSchemaVersion(_:_:_:)`, opening an outdated
// Realm will automatically perform the migration and opening the Realm will succeed
// i.e. Realm()
At the very minimum all we need to do is to update the version with an empty block to indicate the that the schema has been upgraded (automatically) by Realm.
While this is the minimum acceptable migration, we probably want to use this block to populate any new properties (in this case fullName
) with something meaningful. Within the migration block we can call Migration().enumerate(_:_:)
to enumerate each Object of a certain type, and apply any necessary migration logic. Notice how for each enumeration the existing Object instance is accessed via an oldObject
variable and the updated instance is accessed via newObject
:
// Inside your application(application:didFinishLaunchingWithOptions:)
setSchemaVersion(1, Realm.defaultPath, { migration, oldSchemaVersion in
if oldSchemaVersion < 1 {
// The enumerate(_:_:) method iterates
// over every Person object stored in the Realm file
migration.enumerate(Person.className()) { oldObject, newObject in
// combine name fields into a single field
let firstName = oldObject!["firstName"] as! String
let lastName = oldObject!["lastName"] as! String
newObject!["fullName"] = "\(firstName) \(lastName)"
}
}
})
Once the migration is successfully completed, the Realm and all of its objects can be accessed as usual by your app.
Adding more versions
Suppose now we have two previous versions of the Person
class:
// v0
class Person: Object {
dynamic var firstName = ""
dynamic var firstName = ""
dynamic var age = 0
}
// v1
class Person: Object {
dynamic var fullName = "" // new property
dynamic var age = 0
}
// v2
class Person: Object {
dynamic var fullName = ""
dynamic var email = "" // new property
dynamic var age = 0
}
The logic in our migration block might look like the following:
setSchemaVersion(2, Realm.defaultPath, { migration, oldSchemaVersion in
// The enumerateObjects:block: method iterates
// over every 'Person' object stored in the Realm file
migration.enumerate(Person.className()) { oldObject, newObject in
// Add the `fullName` property only to Realms with a schema version of 0
if oldSchemaVersion < 1 {
let firstName = oldObject!["firstName"] as! String
let lastName = oldObject!["lastName"] as! String
newObject!["fullName"] = "\(firstName) \(lastName)"
}
// Add the `email` property to Realms with a schema version of 0 or 1
if oldSchemaVersion < 2 {
newObject!["email"] = ""
}
}
})
// Realm will automatically perform the migration and opening the Realm will succeed
let realm = Realm()
For a more complete look at the implementation of a data schema migration, check out our migration sample app.
Linear Migrations
Suppose we have two users for our app: JP and Tim. JP updates the app very often, but Tim happens to skip a few versions. It’s likely that JP has seen every new version of our app, and every schema upgrade in sequence: he downloaded a version of the app that took him from v0 to v1, another update that took him from v1 to v2. In contrast, it’s possible that Tim will download an update of the app that would need to take him from v0 to v2 immediately. Structuring your migration blocks with non-nested if (oldSchemaVersion < X)
calls ensures that they will see all necessary upgrades, no matter which schema version they start from.
Another scenario may arise in the case of users who skipped versions of your app. If you delete a property email
at version 2 and re-introduce it at version 3, and a user jumps from version 1 to version 3, Realm will not be able to automatically detect the deletion of the email
property, as there will be no mismatch between the schema on disk and the schema in the code for that property. This will lead to Tim’s Person object having a v3 address property that has the contents of the v1 address property. This may not be a problem, unless you changed the internal storage representation of that property between v1 and v3 (say, went from an ISO address representation to a custom one). To avoid this, we recommend you nil out the email
property on the if (oldSchemaVersion < 3)
statement, guaranteeing that all realms upgraded to version 3 will have a correct dataset.
Encryption
Realm’s encryption APIs are available for iOS, OS X & WatchKit but not watchOS because the <mach/mach.h>
and <mach/exc.h>
APIs used by Realm’s encryption mechanism are marked as __WATCHOS_PROHIBITED
.
We’ve filed a radar about this issue: rdar://22063654.
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.
On iOS, it’s possible to encrypt Realm files with very little performance overhead by using NSFileProtection
APIs. There are two main caveats to this approach: 1) the Realm file won’t be portable to other platforms (NSFileProtection
is iOS-only) and 2) the Realm file won’t be encrypted on iOS devices that aren’t password-protected. To avoid these restrictions (or if you’re building an OS X app), then you should use Realm-level encryption.
Realm supports encrypting the database file on disk with AES-256+SHA2 by supplying a 64-byte encryption key when creating a Realm.
// Generate a random encryption key
let key = NSMutableData(length: 64)!
SecRandomCopyBytes(kSecRandomDefault, UInt(key.length),
UnsafeMutablePointer<UInt8>(key.mutableBytes))
// Open the encrypted Realm file
var error: NSError?
let realm = Realm(path: Realm.defaultPath,
readOnly: false, encryptionKey: key, readOnly: false, error: &error)
if realm == nil {
// If the encryption key is wrong, `error` will say that it's an invalid database
println("Error opening realm: \(error)")
return
}
// Use the Realm as normal
let dogs = realm.objects(Dog).filter("name contains 'Fido'")
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 can also have Realm store an encryption key in memory to use automatically whenever opening a Realm at a given path. For example, to set the key for the default Realm (letting you use the convenience methods):
// Generate a random encryption key
let key = NSMutableData(length: 64)!
SecRandomCopyBytes(kSecRandomDefault, UInt(key.length),
UnsafeMutablePointer<UInt8>(key.mutableBytes))
// Set the encryption key for the default Realm
Realm.setEncryptionKey(key, forPath: Realm.defaultPath)
// Use the Realm as normal
let dogs = realm.objects(Dog).filter("name contains 'Fido'")
See our encryption sample app for an end-to-end app that generates an encryption key, stores it securely in the keychain, and uses it to encrypt a Realm.
There is a small performance hit (typically less than 10% slower) when using encrypted Realms.
Note that third party crash reporters (Crashlytics, PLCrashReporter etc.) should be registered before you first open an encrypted Realm, or you may get spurious reports of errors when the app didn’t actually crash.
Debugging
Debugging apps using Realm’s Swift API must be done through the LLDB console.
Note that although the LLDB script installed via our Xcode Plugin allows inspecting the contents of your Realm variables in Xcode’s UI, this doesn’t yet work for Swift. Instead, those variables will show incorrect data. You should instead use LLDB’s po
command to inspect the contents of data stored in a Realm.
Debugging Encrypted Realms
Attaching an LLDB session to a process using an encrypted Realm is not currently supported. In some cases, you may work around this by setting REALM_DISABLE_ENCRYPTION=YES
in your environment; this variable forces all encrypted API methods to work without encryption, allowing you to debug without having to change which Realm APIs you call in your app code. For obvious security reasons, this workaround will not work if you try to access an existing .realm
file that was previously encrypted (this will result in an exception: File::AccessError
), but it might be useful in cases where your app creates & accesses new Realm files.
Testing
Overriding the Default Realm Path
The easiest way to use and test Realm apps is to use the default realm. To avoid overriding application data when testing, simply set the default path (Realm.defaultPath = "/path/to/file.realm"
) before running your tests.
Injecting Realm Instances
Another way to test realm-related code is to have all the methods you’d like to test accept a Realm instance as an argument, so that you can pass in different realm when running the app and when testing it. For example, suppose your app has a method to GET
a user profile from a JSON API and you’d like to test that the local profile is properly created:
// Application Code
static func updateUserFromServer() {
let url = NSURL(string: "http://myapi.example.com/user")
NSURLSession.sharedSession().dataTaskWithURL(url) { data, _, _ in
self.createOrUpdateUserInRealm(Realm(), withData: data)
}
}
public static func createOrUpdateUserInRealm(realm: Realm, withData data: NSData) {
let object: [String: String] =
NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil)
realm.write {
realm.create(object, update: true)
}
}
// Test Code
func testThatUserIsUpdatedFromServer() {
let testRealm = Realm(path: kTestRealmPath)
let jsonString: NSString = "{\"email\": \"help@realm.io\"}"
let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)
ClassBeingTested.createOrUpdateUserInRealm(testRealm, withData: jsonData)
let expectedUser = User()
expectedUser.email = "help@realm.io"
XCTAssertEqual(realm.objects(User).first!,
expectedUser,
"User was not properly updated from server.")
}
Avoid Linking Realm and Tested Code in Test Targets
Since you’re using Realm as a dynamic framework, you’ll need to make sure your unit test target can find Realm. You can do this by adding the parent path to RealmSwift.framework
to your unit test’s “Framework Search Paths”.
If your tests fail with an exception message "Object type '...' not persisted in Realm"
, it’s likely because you’ve linked the Realm framework directly to your test target, which should not be done. Unlinking Realm from your test target should address that.
You should also make sure to only link your model class files to your application or framework targets; never your unit test targets. Otherwise, those classes will be duplicated when testing, which can lead to difficult to debug issues (see https://github.com/realm/realm-cocoa/issues/1350 for details).
You’ll need to make sure all the code you need to test is exposed to your unit test targets (use the public
ACL in Swift).
Resetting State Between Tests
It’s generally useful for unit tests to be isolated from each other. For that reason, we recommend that you delete the persisted realm files from disk and reset Realm’s internal state between every test. This can be done in the setUp
and tearDown
methods when using XCTest:
// Helpers
let realmPathForTesting = ""
func deleteRealmFilesAtPath(path: String) {
let fileManager = NSFileManager.defaultManager()
fileManager.removeItemAtPath(path, error: nil)
let lockPath = path + ".lock"
fileManager.removeItemAtPath(lockPath, error: nil)
}
// In XCTestCase subclass:
override func setUp() {
super.setUp()
deleteRealmFilesAtPath(realmPathForTesting)
Realm.defaultPath = realmPathForTesting
}
override func tearDown() {
super.tearDown()
deleteRealmFilesAtPath(realmPathForTesting)
}
REST APIs
Realm easily integrates with REST APIs and provides several advantages as oppposed to using REST APIs without local persistance:
- Caching your data in Realm allows you to provide an offline experience, as opposed to REST APIs where connectivity is always required.
- By caching your entire data set in Realm, you can execute queries locally and provide a better search and browsing experience than wouldn’t be possible with REST alone.
- Storing your data in Realm can reduce server-side load by only fetching new or changed data.
Best Practices
- Asynchronous Requests — Network requests and other blocking operations should be performed on a background thread to avoid blocking the user interface. For the same reason it is recommended that inserting or changing a large number of objects in a Realm are made on a background thread. You can use Notifications to respond to changes made in the background.
- Caching large datasets — We recommend you pre-fetch data when possible and store it locally in a Realm. This allows you to perform queries over your entire dataset locally.
- Insert-or-update — If your dataset has a unique identifier, such as a primary key, you can use it to easily implement insert-or-update logic using
Realm().add(_:update:)
: when receiving a response from a REST API. These methods automatically check if each record already exist and apply updates to existing records while creating new records.
Example
The following is a simple example of how you can use Realm with a REST API. In this example, we’ll retrieve a JSON-formatted data set from the foursquare API, then save it as Realm Objects in the default Realm.
For a realtime example of a similar use case in action, check out our video demo.
First we create an instance of the default Realm to persist the data to, and fetch our data set from the API. For simplicity in this example we use NSData(contentsOfURL:)
.
// Call the API
let url = NSURL(string: "https://api.foursquare.com/v2/venues/search?near=San%20Francisco&limit=50")!
let response = NSData(contentsOfURL: url)!
// De-serialize the response to JSON
let json = (NSJSONSerialization.JSONObjectWithData(response,
options: NSJSONReadingOptions(0),
error: nil) as! NSDictionary)["response"]
The response contains a JSON array of venues similar to this:
{
"venues": [
{
"id": "4c82f252d92ea09323185072",
"name": "Golden Gate Park",
"contact": {
"phone": "4152522590"
},
"location": {
"lat": 37.773835608329,
"lng": -122.41962432861,
"postalCode": "94103",
"cc": "US",
"state": "California",
"country": "United States"
}
}
]
}
There are several ways we may want to import this JSON into our Realm. You could read the NSDictionary and map the properties to a single Object manually via a custom insert function. For the sake of this example, we will instead directly insert the NSDictionary in the Realm and have it automatically be mapped to a hierarchy of Objects that will be created on the fly for us. For this to work, we need an Object structure whose properties will match all the keys in the JSON exactly. JSON keys not matched by an Object property will be ignored on insert. The following Object declarations would work:
class Contact: Object {
dynamic var phone = ""
static func primaryKey() -> String? {
return "phone"
}
}
class Location: Object {
dynamic var lat = 0.0 // latitude
dynamic var lng = 0.0 // longitude
dynamic var postalCode = ""
dynamic var cc = ""
dynamic var state = ""
dynamic var country = ""
}
class Venue: Object {
dynamic var id = ""
dynamic var name = ""
dynamic var contact = Contact()
dynamic var location = Location()
static func primaryKey() -> String? {
return "id"
}
}
Since the result set is given to us as an array we have to create an object for each element by calling Realm().create(Venue.self, value: value)
. This creates Venue
and its child objects from a JSON representation and adds the newly created objects to the default Realm:
//Extract the array of venues from the response
let venues = json["venues"] as! [NSDictionary]
let realm = Realm()
realm.write {
// Save one Venue object (and dependents) for each element of the array
for venue in venues {
realm.create(Venue.self, value: venue, update: true)
}
}
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.
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.
- NSData 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.
- NSDate properties can only persist date information down to one second. Refer to the NSDate entry in Current Limitations below for more information.
- Any single Realm file 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 map it over multiple Realm files.
Fine-grained notifications are not yet supported
While it is possible to receive notifications when a realm changes (see Notifications), it is not currently possible to determine what was added/removed/moved/updated from that notification. We will be adding this feature in the near future.
NSDate is truncated to the second
Persisting an NSDate
with a fractional number of seconds will truncate the date to the second. A fix for this is in progress. See GitHub issue #875 for more details. In the mean time, you can store NSTimeInterval
properties with no loss in precision.
Realm Object Setters & Getters cannot be overriden
Since Realm overrides setters and getters to back properties directly by the underlying database, you cannot override them on your objects. A simple workaround is to create new, realm-ignored properties, whose accessors can be overriden, and can call other setters/getters.
KVO is not supported
Realm doe not support KVO yet but has its own notification mechanism (see Notifications). We are working to add KVO support at the moment: see GitHub issue #601.
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 Realm
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, or may be compacted — for example by calling Realm().writeCopyToPath(_:encryptionKey:)
.
To avoid this issue you, may call invalidate
to tell Realm that you no longer need any of the objects that you’ve read from the Realm so far, which frees us from tracking intermediate versions of those objects. The Realm will update to the latest version the next time it is accessed.
You may also see this problem when accessing Realm using Grand Central Dispatch. This can happen when a Realm ends up in a dispatch queue’s autorelease pool as those pools may not be drained for some time after executing your code. The intermediate versions of data in the Realm file cannot be reused until the Realm
object is deallocated. To avoid this issue, you should use an explicit autorelease pool when accessing a Realm from a dispatch queue.
List properties aren’t accessible from Objective‑C
If you’re planning on using Realm Swift from Objective‑C, you’ll have to make your List
properties private
or internal
. This is due to a known Swift bug which causes the automatically-generated Objective‑C header (-Swift.h
) to be unable to compile. See GitHub issue #1925 for more details and suggested workarounds.
FAQ
How big is the Realm library?
Once your app is built for release, Realm should only add around 1MB to its size. The releases we distribute are significantly larger (~37MB for iOS & ~2.4MB for OS X) because they include support for more architectures (ARM, ARM64, x86 for the simulator) and some debug symbols, which will all be stripped by Xcode automatically when you build your app.
Should I use Realm in production applications?
Realm has been used in production in commercial products since 2012.
You should expect our Objective‑C & Swift 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-cocoa, we’re always happy to chat by email. Otherwise, we are committed to developing realm-cocoa 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. In the meantime, its binary releases are made available under the Realm Core (TightDB) Binary License.