preface

For those of you who don’t know what Realm is, let me give you a quick overview of the new database. Touted as a replacement for SQLite and Core Data. Realm has the following advantages:

  1. Convenience Realm is not an OBJECT-relational mapping database based on SQLite. It uses its own persistence engine and is built for simplicity and speed. Users said they were up and running with Realm in minutes, building an app in hours and saving at least weeks of development time per app.

  2. Fast Realm is faster than other Object Relational Mapping databases and even native SQLite, thanks to its zero-copy design. What are iOS and Android users saying about Twitter

  3. Cross-platform Realm supports iOS and OS X (Objective C & Swift) and Android. You can share Realm files to all platforms, Java, Swift, objective-C, by using the same Model. And the same business logic can be used across the platform

  4. Great features Realm supports advanced features such as encryption, graphical queries, and easy migration. Realm’s API is perfect for building highly responsive applications, and Realm provides convenient components to easily build complex user interfaces

  5. Trusted Realm has already found favor with banks, healthcare providers, sophisticated enterprise apps, and Starbucks.

  6. Community-driven Realm is the fourth most star-studded database on Github, behind Java and Cocoa’s Repos. In addition to the core project, the Realm community has compiled hundreds of App plug-ins and components

  7. Support get official answers quickly from Realm, Inc., to compile and support your database. Realm’s team answers your questions on Github, StackOverflow, & Twitter

Here are 3 more amazing performance comparisons


The figure above shows the number of times count can be queried in 200,000 data items per second. Realm can perform 30.9 queries per second after count. SQLite only has count after 13.6 queries per second, compared to a measly 1 for Core Data.


A traversal query in 200,000 entries is similar to count: A Realm can traverse 200,000 entries 31 times a second, while RCore Data can only traverse 200,000 entries twice. SQLite only had 14.


This is a comparison of data inserts per second in a single transaction. Realm can insert 94,000 records per second, where pure SQLite performs best at 178,000 records per second. While FMDB, which encapsulates SQLite, scored about half that of Realm at 47k, Core Data was even lower ata measly 18,000.

You can see some of the great features of Realm in these three images. So let’s start using Realm. The first step is to replace the local database with a Realm.

The following is a manual tutorial I translated, so let’s go through the tutorial and migrate Core Data to Realm.

The original

The translation

Migrating to Realm from an app that uses the Core Data framework as a way to store its database is fairly straightforward. If you have an app that already uses Core Data and are considering switching to Realm, this hands-on tutorial is for you!

Many developers have highly integrated Core Data user interfaces (sometimes thousands of lines of code), and many will tell you that converting Core Data to Realm can take hours. Both Core Data and Realm treat your Data as objects, so migration is usually straightforward: reconstructing your existing Core Data code using the Realm API is straightforward.

After the migration, you’ll be excited about the ease of use, speed, and stability that Realm brings to your apps.

1. Remove the Core Data Framework

First of all, if your app is currently using Core Data, you need to find out which code contains Core Data. This code is refactored. Fortunately, there’s a manual way to do this: you can manually search the entire code for relevant code and delete every statement that imports the Core Data header declaration

#import <CoreData/CoreData.h>
//or
@import CoreData;
Copy the code

Once this is done, each row that uses Core Data will report a compilation error, and it’s only a matter of time before these compilation errors are resolved.

2. Remove Core Data setup code

In Core Data, changes to model objects are made through managed Object Context Objects. Managed Object Context Objects are created by persistent Store Coordinator Objects, and both are created by managed Object Model Objects.

So to speak, when you start thinking about reading or writing Data with Core Data, you usually need to set up dependencies somewhere in your app, exposing Core Data methods for your app logic to use. Whether in your Application Delegate, global singletons, or even inline implementations, there’s a lot of potential Core Data setup code.

All of this code can be removed when you are ready to switch to Realm.

In a Realm, all Settings are done the first time you create a Realm Object. It can also be configured manually, as you specify which path to store the Realm data files on your hard drive. This can be done at Runtime.

RLMRealm *defaultRealm = [RLMRealm defaultRealm];
//or
let realm = Realm()
Copy the code

Does that feel good?

3. Migrate model files

In Core Data, the classes that you use are all subclasses that are defined as NS-managed object. The interfaces for these objects are quite standard. Primitive types (such as NSInteger and CGFloat) cannot be used and must be abstracted into an NSNumber object.

@interface Dog : NSManagedObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSNumber *age;
@property (nonatomic, strong) NSDate *birthdate;

@end

@implementation Dog

@dynamic name;
@dynamic age;
@dynamic birthdate;

@end
Copy the code

Converting these managed Object subclasses to a Realm is quite simple:

@interface Dog : RLMObject

@property NSString *uuid;
@property NSString *name;
@property NSInteger age;
@property NSDate *birthdate;

@end

@implementation Dog

+ (NSString *)primaryKey
{
    return @"uuid";
}

+ (NSDictionary *)defaultPropertyValues
{
    return @{ @"uuid" : [[NSUUID UUID] UUIDString],
              @"name" : @"",
              @"birthdate" : [NSDate date]};
}

@end
Copy the code

or


class Dog: Object {
    dynamic var uuid = NSUUID().UUIDString
    dynamic var name = ""
    dynamic var age = 0
    dynamic var birthdate = NSDate().date
    
    override static func primaryKey() -> String? {
        return "uuid"
    }
}
Copy the code

Done! How simple is that?

Looking at these implementations, there are a few Realm details to note.

For first-time users of realms, there is no need to specify property keywords; realms are already managed internally. So the header files of these classes look very compact. In addition, Realm supports simple data types, such as NSInteger and CGFloat, all of which can be safely deleted.

On the other hand, there are some additional notes about Realm Model declarations.

  1. Core Data Objects uniquely identifies an object through its internal NS-Managed DobJectid property. Realm leaves this up to the developer. In the example above, we add an additional attribute called uUID, and then call the [RLMObject primaryKey] method as the unique identifier for the class. Of course, if your objects don’t need unique identification at all, you can skip all of this.

  2. In the process of writing data (which won’t take long!) Realm can’t handle properties of nil objects. The reason is that the class method [RLMObject defaultPropertyValues] defines a set of default values for each object property when it is initially created. While this is only temporary, we are happy to tell you that in a future update we will be supporting Realm Object properties that can be nil.

4. Migrate write operations

If you can’t save your data, this is certainly not a durable solution! To create a new Core Data object and then simply modify it, you need the following code:


//Create a new Dog
Dog *newDog = [NSEntityDescription insertNewObjectForEntityForName:@"Dog" inManagedObjectContext:myContext];
newDog.name = @"McGruff";

//Save the new Dog object to disk
NSError *saveError = nil;
[newDog.managedObjectContext save:&saveError];

//Rename the Dog
newDog.name = @"Pluto";
[newDog.managedObjectContext save:&saveError];
Copy the code

In contrast, the operation of Realm saves is slightly different, but there are still similarities in modifying the code above in the same scope.

//Create the dog object
Dog *newDog = [[Dog alloc] init];
newDog.name = @"McGruff";

//Save the new Dog object to disk (Using a block for the transaction)
RLMRealm *defaultRealm = [RLMRealm defaultRealm];
[defaultRealm transactionWithBlock:^{
    [defaultRealm addObject:newDog];
}];

//Rename the dog (Using open/close methods for the transaction)
[defaultRealm beginWriteTransaction];
newDog.name = @"Pluto";
[defaultRealm commitWriteTransaction];
Copy the code

or

//Create the dog object
let mydog = Dog()
myDog.name = "McGruff"

//Save the new Dog object to disk (Using a block for the transaction)
Realm().write {
    realm.add(myDog)
}

//Rename the dog (Using open/close methods for the transaction)
Realm().beginWrite()
myDog.name = "Pluto"
Realm().commitWrite()
Copy the code

Done! Our data is saved!

The obvious difference is that in realms, once a object has been added to a Realm object, it cannot be modified. A Realm Object is stored in a write transaction for execution after the property modification operation. This model, which cannot be modified, ensures data consistency when different threads read/write object data.

The implementation of Core Data can indeed change properties and then call the save method compared to the implementation of Realm, just a few minor differences.

5. Migrate queries

On the other hand, if you can’t retrieve queries for your data, this is certainly not a durable solution!

In the basic implementation of Core Data, it uses the concept of Fetch Requests to retrieve Data from hard disk. A Fetch Request object is created as a single instantiated object, containing some additional filtering parameters and sorting conditions.


NSManagedObjectContext *context = self.managedObjectContext;

//A fetch request to get all dogs younger than 5 years old, in alphabetical order
NSEntityDescription *entity = [NSEntityDescription
                               entityForName:@"Dog" inManagedObjectContext:context];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age < 5"];

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];

NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = entity;
request.predicate = predicate;
request.sortDescriptors = @[sortDescriptor];

NSError *error;
NSArray *dogs = [moc executeFetchRequest:request error:&error];
Copy the code

That’s great, but it takes a lot of code! Some clever developers have developed libraries to make this code easier to write. Such as MagicalRecord.

By comparison, the equivalent code for these queries with Realm is as follows:

RLMResults *dogs = [[Dog objectsWhere:@"age < 5"] sortedResultsUsingProperty:@"name" ascending:YES];
Copy the code

or

var dogs = Realm().objects(Dog).filter("age < 5").sorted("name")
Copy the code

Two methods are called in one line. Nearly 10 lines of code compared to Core Data.

Of course, the results of the same operation are the same (RLMResults and NSArray are basically similar), and since these queries are very independent, the logic around the query requires very little refactoring.

6. Migrate user data

Once you’ve migrated all your code to Realm, there’s another outstanding question, how do you migrate all the Data that users already have on their devices from Core Data to Realm?

Obviously, this is a very complex issue, and it depends on the functionality of your app and the user’s environment. You may have a different solution each time you handle the situation.

So far, we see two scenarios:

  1. Once you have migrated to a Realm, you can re-import the Core Data Framework into your app, fetch your user’s Core Data using nS-managed Object Objects, and manually upload the Data to the Realm. You can leave the migrated code in the app permanently, or you can delete it after a very long time.

  2. If the user Data is not irreplaceable — for example, if it’s a simple cache of information that can be regenerated from the user Data on the hard drive — then it’s easy to just wipe out the Core Data and start from zero the next time the user opens the app. Of course this needs to be considered very carefully, otherwise it will leave a very bad user experience for many people.

Ultimately, the decision should be biased in favor of the user. Ideally, you don’t want to leave Core Data still connected to your app, but the outcome will depend on your situation. Good luck!

Further discussion

While there are no really important steps in porting an application to Realm, there are a few additional cases you should know:

concurrent

If you do some heavy work in background threads, you may find that you need to pass Realm Objects between threads. In Core Data you are allowed to pass managed Objects between threads (although it is not a best practice to do so), but in Realm passing objects between threads is strictly prohibited, and any attempt to do so will throw a serious exception.

In this case, it is easy to do the following:


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    //Rename the dog in a background queue
    [[RLMRealm defaultRealm] transactionWithBlock:^{
        dog.name = @"Peanut Wigglebutt";
    }];
    
    //Print the dog's name on the main queue
    NSString *uuid = dog.uuid;
    dispatch_async(dispatch_get_main_queue(), ^{
        Dog *localDog = [Dog objectForPrimaryKey:uuid];
        NSLog(@"Dog's name is %@", localDog.name);
    });
});
Copy the code

or


dispatch_async(queue) {
    //Rename the dog in a background queue
    Realm().write {
        dog.name = "Peanut Wigglebutt"
    }
    
    //Print the dog's name on the main queue
    let uuid = dog.uuid
    dispatch_async(dispatch_get_main_queue()) {
        let localDog = Realm().objectForPrimaryKey(Dog, uuid)
        println("Dog's name is \\(localDog.name)")
    }
}
Copy the code

While Realm Objects cannot be passed between threads, copies of Realm Properties can be passed between threads. Considering that Realm is very fast at retrieving objects from disk, there is only a small performance penalty if you simply refetch the same objects in storage via a new thread. In this example, we take a copy of the primary key of the object, pass it from the background queue to the main queue, and then use it to retrieve the object in the context of the main thread.

NSFetchedResultsController equivalent approach

Compared with Core Data of all faults, may use the Core Data is the most adequate grounds NSFetchedResultsController – this is a class, it can detect the change of the Data storage, and can automatically to show the change to the UI.

At the time of this writing, Realm does not have a similar mechanic. Although it can register a block that is executed when the data source changes, this “brute force” approach is not friendly to most UIs. Right now, if your UI code relies heavily on Realm, this is like dealing with a breaker for you.

Cocoa engineers at Realm are currently working on a notification system that allows us to register notifications when some object properties are changed to receive those changes. These features will be in future updates to Swift and Objective C for Realm.

During this period, if the notice of the existing block API or does not meet your need, but you still needed to be changed when a particular property received a notice, here it is recommended to use magic third-party libraries, name is RBQFetchedResultsController, it can imitate the function. In addition, you can achieve the same functionality by adding setter methods to objects that send a broadcast notification when the setter method is called.

At the end

Both Core Data and Realms display Data through Model Objects, and this similarity makes migrating from Core Data to Realms very quick and easy (and very satisfying!). . Although it may seem daunting at first, in practice you need to convert each Core Data method call to the equivalent Realm method, and then write a helper class to help you migrate user Data. These are also very simple.

If you’re having trouble using Core Data in your app and need a simpler solution, we highly recommend you try Realm to see if it works for you. If applicable, please let us know!

Thanks for reading this article. Go build an amazing app with Realm! You can reach us on StackOverflow, GitHub, or Twitter.