is a truly truly mobile database. mobile database. Realm is a Realm is a replacement replacement for SQLite, Core for SQLite, Core Data, JSON/Property List files. Data, JSON/Property List files. Realm is easy to use, Realm is easy to use, fast fast, cross-platform , cross-platform and well supported. and well supported. 1 1
SQLite FMDB is cool... ... said someone in 2011. Core Data Learning curve anyone? ORM on top of SQLite Complex API Offline JSON/Plist files Ok for small and simple transactions Limited Flexibility Too much I/O Thread-safety? ACID?
Realm is not an ORM on top of SQLite/CoreData/x Custom C++ core with bit-packing, caching, vectorization and zero-copy architecture. It has it's own conventions and data-structures that are easy to grasp and use. Basically just 3 classes (Object, Arrays and Realms) You can use Realm models convention to write all your app models if you want. More later...
truly innovative MVCC MVCC Native Links Native Links Crash Safety Crash Safety Zero Copy Zero Copy True Lazy-Loading True Lazy-Loading Built from scratch with mobile platforms and Built from scratch with mobile platforms and limitations in mind limitations in mind Why aim to be massively distributed? Why aim to be massively distributed? Why aim to be shardable across instances? Why aim to be shardable across instances? Why be low latency over connection sockets? Why be low latency over connection sockets?
down. Realm is objects all the way down. Most existing solutions are ORMs on top of Most existing solutions are ORMs on top of something. something. ORMs abstract what's going underneath ORMs abstract what's going underneath Objects become records/rows in tables with Objects become records/rows in tables with foreign keys and primary keys. foreign keys and primary keys. The abstraction kind of falls apart because The abstraction kind of falls apart because you need to start traversing relationships you need to start traversing relationships using expensive operations. using expensive operations.
already "connected" When you init a Realm, you're already "connected" to the database to the database Actually a fraction of the database is being Actually a fraction of the database is being memory mapped, there's no socket whatsoever. memory mapped, there's no socket whatsoever. As soon as you add an object (record) in Realm, it As soon as you add an object (record) in Realm, it becomes an accessor. becomes an accessor. Once you start reading properties from it, you are Once you start reading properties from it, you are not accessing your iVars! not accessing your iVars! You access raw database values avoiding lots of You access raw database values avoiding lots of memory copying. memory copying.
Native Links) Writer Reader HEAD HEAD~1 The file structure is just a tree of The file structure is just a tree of links. links. Raw pointers to objects. Raw pointers to objects. Same if you're querying for an Int, Same if you're querying for an Int, String or relationship! String or relationship!
As you make changes, you're forking the tree. The top level pointer "HEAD" is always pointing to the latest non-corrupt tree. When Realm verifies the newest write transaction (fsync()) it move's the top level pointer and get a new "official version"
ORMs deal with data Data sits on disk. Data sits on disk. You access a CoreData model's property: You access a CoreData model's property: Translate request into a set of SQL statements. Translate request into a set of SQL statements. Create database connection. Create database connection. Perform the SQL query. Perform the SQL query. Read all the data from the rows that match the Read all the data from the rows that match the query and bring it to memory. query and bring it to memory. Deserialize from the disk format to something you Deserialize from the disk format to something you can represent in memory (align bits). can represent in memory (align bits). Convert to the language type. Convert to the language type.
does it The data is still on disk but it's memory mapped. The data is still on disk but it's memory mapped. You can access any offset in the file as if it was in You can access any offset in the file as if it was in memory, although it's not. It's in virtual memory. memory, although it's not. It's in virtual memory. Objects all the way down, everything is word-aligned Objects all the way down, everything is word-aligned (limitations) (limitations) The core file format is readable in memory without The core file format is readable in memory without requiring deseriliazation (core Realm principle) requiring deseriliazation (core Realm principle) No copy to memory! No copy to memory! All you need is calculate the offset of the data to read All you need is calculate the offset of the data to read in the mmap, read the value from offset up to offset + in the mmap, read the value from offset up to offset + propery.length and return the raw value from the propery.length and return the raw value from the property access! property access!
don't allow to read just 1 bit form Hardware limitations don't allow to read just 1 bit form disk disk Even if you want a Boolean property, you need to Even if you want a Boolean property, you need to load the disk's page size, nothing smaller than that. load the disk's page size, nothing smaller than that. Most databases store things on a horizontal, linear Most databases store things on a horizontal, linear level. level. When you want a property from SQLite, you still When you want a property from SQLite, you still have to load the entire row because it's stored have to load the entire row because it's stored contiguously in the file. contiguously in the file. Realm stores properties contiguously linked at the Realm stores properties contiguously linked at the vertical level in vectors (columns vs rows) vertical level in vectors (columns vs rows) An object's property is never in an iVar unless you An object's property is never in an iVar unless you actually try to read it or modify it. actually try to read it or modify it.
Int32, Int64 Storing optional numbers is done using RealmOptional Double, Float String (can be optional) NSDate (truncated to the second, can be optional) NSData (can be optional)
dynamic var id = "" dynamic var firstname = "" dynamic var lastname = "" dynamic var nickname: String? = nil dynamic var email = "" dynamic var password = "" dynamic var registrationDate = NSDate(timeIntervalSince1970: 1) override static func primaryKey() -> String? { return "id" } } Or just map the id to your server Or just map the id to your server database's id. database's id.
class User: Object { dynamic var id = "" dynamic var firstname = "" dynamic var lastname = "" dynamic var nickname: String? = nil dynamic var email = "" dynamic var password = "" dynamic var registrationDate = NSDate(timeIntervalSince1970: 1) let posts = List<Post>() override static func primaryKey() -> String? { return "id" } override static func indexedProperties() -> [String] { return ["id", "firstName"] } } Greatly speeds up queries at Greatly speeds up queries at the cost of slower insertions. the cost of slower insertions.
Realm() user.lastname = "Papadopoulos" try! realm.write { realm.add(user, update: true) } This will work only if the User object has a primary key. If not, you need to enclose `user.lastname` in a `write()` block. If you omit `update: true` and an object with that key already exists, you get an exception (if unhandled, you crash)
collection of Objects. All queries are lazy. Data is only read when the properties are accessed. Results are not copies. Modifying a Results object within a write transaction will persist to disk. Query execution is deferred until the results are used. Once a query has been executed, the Results object is up to date with any other changes in Realm.
Realm() // Forward relationship let user = realm.objects(User).first let posts = user.posts // Inverse (backward) relationship let post = realm.objects(Post).first let postUser = post.postOwners.first
Realm() // Get a user with a matching id let predicate = NSPredicate(format: "id == %@", id) let user1 = realm.objects(User).filter(predicate) // Get users with GMail accounts predicate = NSPredicate(format: "email ENDSWITH %@", "gmail.com") let users = realm.objects(User).filter(predicate) // Get top active users predicate = NSPredicate(format: "[email protected] > 100") let activeUsers = realm.objects(User).filter(predicate) // Get users registered before Christmas predicate = NSPredicate(format: "date <= %@", christmasDate)
default Realm database. A file named `default.realm` in /Documents var config = Realm.Configuration() config.path = NSURL.fileURLWithPath(config.path!) .URLByDeletingLastPathComponent? .URLByAppendingPathComponent("NewName.realm") .path // set tihs as the config for the default realm Realm.Configuration.defaultConfiguration = config
as many different Realms as you want! let config = Realm.Configuration( path: myPathToBackupRealm.path readOnly: false ) // Default Realm (default.realm) let realm = try! Realm() // Backup Realm (backup.realm) let otherRealm = try! Realm(configuration: config)
in-memory Realms Cool for caching layers let memoryCache = try! Realm(configuration: Realm.Configuration(inMemoryIdentifier: "com.me.cache") ) Beware of in-memory instances going out of scope with no references. Garbage collector will wipe your data off Keep a strong reference to your in-memory Realms.
prepopulated Realms with your app. In the code where you generate the prepopulated Realm, make sure to "compact" it: Realm().writeCopyToPath(_:encryptionKey:) Drag and drop and add it in "Copy Bundle Resources" of your build phase. If you need to mutate this Realm, your first need to copy it to your application's Documents directory. Bundles are read-only.
try! Realm() for each thread Realm state for each thread will be based off the most recent confirmed transaction commit and will remain there until that version is refreshed. States are automatically refreshed at the start of every NSRunLoop iteration. Background threads that do not have run loops must explicitly call Realm.refresh()
models change... Realm models are parsed and validated Realm models are parsed and validated during compile-time. during compile-time. But, if Realm sees a mismatch in models and But, if Realm sees a mismatch in models and the database during run-time, the database during run-time, it throws an it throws an exception and doesn't let you read or write to exception and doesn't let you read or write to the db. the db. ... Unless you run a migration! ... Unless you run a migration!
User: Object { dynamic var id = "" dynamic var firstname = "" dynamic var lastname = "" dynamic var nickname: String? = nil dynamic var email = "" dynamic var password = "" dynamic var registrationDate = NSDate(timeIntervalSince1970: 1) } // v2 Model class User: Object { dynamic var id = "" dynamic var fullname = "" dynamic var nickname: String? = nil dynamic var email = "" dynamic var password = "" dynamic var registrationDate = NSDate(timeIntervalSince1970: 1) }
on disk Realm supports encrypting the db file on disk with AES-256+SHA2 and 64byte keys with AES-256+SHA2 and 64byte keys let key = NSMutableData(length: 64)! // Generate random key SecRandomCopyBytes( kSecRandomDefault, key.length, UnsafeMutablePointer<UInt8>(key.mutableBytes) ) // Save it securely in keychain // Always pass it in default Realm configuration // to be able to use encrypted Realm file Realm.Configuration.defaultConfiguration.encryptionKey = key