Realm Blog

Realm Cocoa Reaches 3.0: Introducing ROS 2.0, Arrays of Primitives, and More!

We’re proud to introduce version 3.0.0 of Realm Objective-C and Realm Swift, which ships with many new features, improvements, and bug fixes. Read on to find out more about what we’ve added, improved, and deprecated — or head over to see what’s happening on Realm Java or Realm .NET!

Realm Object Server 2.0 support

Now, you can take advantage of all the benefits of ROS 2.0, including improved scalability, better performance, packaging based on npm, and the powerful new Realm Studio development tool for inspecting and administering your synchronized Realms! Beginning with this release, if you are using synchronized Realms, Realm Objective-C and Realm Swift will now require Realm Object Server version 2.0 or later.

If you were previously running Realm Object Server 1.x and wish to upgrade to Realm Object Server 2.0, you should read our documentation (Objective-C, Swift) on how to properly handle migrations on the client!

Read more about Realm Object Server 2.0 here.

Arrays of primitives

You can now store primitive types or their nullable counterparts (more specifically: booleans, integer and floating-point number types, strings, dates, and data) directly within RLMArrays or Lists. If you want to define a list of such primitive values you no longer need to define cumbersome single-field wrapper objects. Instead, you can just store the primitive values themselves!

Lists of primitive values work much the same way as lists containing objects, as the example below demonstrates.

class Student : Object {
	@objc dynamic var name: String = ""
	let testScores = List<Int>()
}

// Retrieve a student.
let realm = try! Realm()
let bob = realm.objects(Student.self).filter("name = 'Bob'").first!

// Give him a few test scores, and then print his average score.
try! realm.write {
    bob.testScores.removeAll()
    bob.testScores.append(94)
    bob.testScores.append(89)
    bob.testScores.append(96)
}
print("\(bob.testScores.average()!)")   // 93.0

Check out our documentation on many-to-many relationships (Objective-C, Swift) for more information. Note that we don’t currently support querying lists of primitives, but plan to add this functionality in a future release.

Partial synchronization

We’re introducing an early version of Realm’s new Partial Synchronization feature. Though it’s still an experimental feature that we’re actively changing and improving, we think it’s an important new pattern that enables much more flexible architectures on Realm.

The Realm Object Server currently keeps synchronized Realms fully synchronized, meaning that all copies of the Realm (whether on the server or device) eventually converge towards containing the same data. While this is great for most use cases, especially if you want updates on one device to be transparently reflected everywhere else, sometimes you want more control over what objects are actually synchronized from the server onto a device.

For example, you might have a large read-only Realm representing a product catalog for a massive e-commerce website, and you might only want to sync to a user’s device those database entries representing some relevant subset of those products (perhaps something like “prosumer DSLR cameras made by Canon that are between $500 and $1000”).

Partial sync allows you to open a synced Realm in “partial synchronization mode.” Such a Realm won’t automatically download all information on the remote server, but will instead only sync down objects that match queries you make. Once those objects are synchronized, they behave like normal synced objects. They’ll update in real time based on remote changes, and any changes you make to those objects will be synced back up to the server.

Here is a toy example demonstrating what partial sync looks like in action:

// First, we specify that we want to open the Realm in partial synchronization mode (isPartial = true)
let config = Realm.Configuration(syncConfiguration:
    SyncConfiguration(user: user, realmURL: productRealmURL, isPartial: true))
let realm = try! Realm(configuration: config)

// Then we can subscribe to queries.
// Queries are made using NSPredicate format.
let query = "price >= 500 AND price >= 1000 AND manufacturer == 'Canon' AND category == 'Cameras'"
realm.subscribe(to: Product.self, where: query) { (results, error) in
    // Results are returned asynchronously.
    guard let results = results else {
        print("An error occurred while fetching the results! \(error!)")
        return
    }
    if let firstCamera = results.first {
        print("The first product result: \(firstCamera.productName), \(firstCamera.productDescription)")
    }
}

Check out our partial synchronization documentation (Objective-C, Swift) for more details.

All permissions operations (retrieving permissions, applying or revoking permissions, creating or accepting permission offers) are now carried out declaratively via APIs on RLMSyncUser. The deprecated ROS 1.0 permissions system requiring permission and management Realms to be manually opened and inspected has been removed. See our documentation on access control (Objective-C, Swift) for more information.

It is now possible to recover from improperly writing to a synced Realm your user doesn’t have read privileges for. A system, much like that for handling client reset errors, has been added, allowing you to safely delete a Realm which has been improperly written to and re-opening it without restarting your application. Check out our documentation (Objective-C, Swift) for more information.

The asynchronous callback when SyncUser.logIn() completes now runs on the main queue by default. This means that you can make changes to your application’s UI directly from the completion handler without needing to dispatch back onto the main queue, and functionality which depends on an active run loop existing on the current thread should work as expected (for example, retrieving permissions or registering notifications on collections).

Realm Object Server administrators who use the -retrieveInfoForUser:... API to retrieve information about a ROS user will now get metadata about the user stored on the server, as well as a list of all authentication provider accounts associated with that user.

API changes

A number of previously deprecated APIs have been removed. In particular, APIs related to sorting results that take a property name have been removed; please use the corresponding APIs that take a key path instead.

Some Swift APIs have been renamed in order to match naming conventions introduced by Swift 4. For example, you must now call observe() instead of addNotificationBlock() on a collection to observe changes, and invalidate() rather than stop() on notification tokens when you wish to stop observing.

Our change log has a comprehensive table detailing what APIs have been removed or renamed, as well as what APIs should be used instead. We have also added Xcode fix-its for Swift APIs that have been changed to make migrating your applications as painless as possible!

Other improvements and bug fixes

Please check out our change log for a full list of improvements. We’ve highlighted a few below, but there are quite a few others that might be of interest!

Swift Object comparison behavior has been updated to match the behavior of RLMObject and respect Foundation’s equatable and hashable requirements. In particular, objects are only checked for equality if a primary key is defined, and it is no longer possible for two objects that are considered equal to return different hash values.

For users using Swift 4, we’ve changed List to conform to the MutableCollection protocol instead of RangeReplaceableCollection. The latter protocol required us to define an empty initializer which didn’t make sense for List’s semantics and produced surprising behavior (for example, when concatenating lists). We’ve added overloads for all the RangeReplaceableCollection default methods that make sense for List. In almost all cases this change should have no effect on your application code.

We’ve also updated the way query predicates using BEGINSWITH, ENDSWITH, and CONTAINS work when their right side operand is nil, an empty string, or data object, to match Foundation’s behavior.


Thanks for reading. Now go forth and build amazing apps with Realm! As always, we’re around on the Realm Forums, Stack Overflow, GitHub, or Twitter.


Realm Cocoa Team

At Realm, our mission is to help developers build better apps faster. We provide a unique set of tools and platform technologies designed to make it easy for developers to build apps with sophisticated, powerful features — things like realtime collaboration, augmented reality, live data synchronization, offline experiences, messaging, and more.

Everything we build is developed with an eye toward enabling developers for what we believe the mobile internet evolves into — an open network of billions of users and trillions of devices, and realtime interactivity across them all.

Get more development news like this