Docs Menu

CRUD - Read - Swift SDK

On this page

A read from a realm generally consists of the following steps:

  • Get all objects of a certain type from the realm.
  • Optionally, filter the results.
  • Optionally, sort the results.

All query, filter, and sort operations return a results collection. The results collections are live, meaning they always contain the latest results of the associated query.

When you design your app's data access patterns around the following three key characteristics of reads in Realm Database, you can be confident you are reading data as efficiently as possible.

Results to a query are not copies of your data: modifying the results of a query will modify the data on disk directly. This memory mapping also means that results are live: that is, they always reflect the current state on disk.

See also: Collections are Live.

Realm Database only runs a query when you actually request the results of that query. This lazy evaluation enables you to write elegant, highly performant code for handling large data sets and complex queries. You can chain several filter and sort operations without requiring extra work to process the intermediate state.

One benefit of Realm Database's object model is that Realm Database automatically retains all of an object's relationships as direct references, so you can traverse your graph of relationships directly through the results of a query.

A direct reference, or pointer, allows you to access a related object's properties directly through the reference.

Other databases typically copy objects from database storage into application memory when you need to work with them directly. Because application objects contain direct references, you are left with a choice: copy the object referred to by each direct reference out of the database in case it's needed, or just copy the foreign key for each object and query for the object with that key if it's accessed. If you choose to copy referenced objects into application memory, you can use up a lot of resources for objects that are never accessed, but if you choose to only copy the foreign key, referenced object lookups can cause your application to slow down.

Realm Database bypasses all of this using zero-copy live objects. Realm object accessors point directly into database storage using memory mapping, so there is no distinction between the objects in Realm Database and the results of your query in application memory. Because of this, you can traverse direct references across an entire realm from any query result.

As a result of lazy evaluation, you do not need any special mechanism to limit query results with Realm Database. For example, if your query matches thousands of objects, but you only want to load the first ten, simply access only the first ten elements of the results collection.

Thanks to lazy evaluation, the common task of pagination becomes quite simple. For example, suppose you have a results collection associated with a query that matches thousands of objects in your realm. You display one hundred objects per page. To advance to any page, simply access the elements of the results collection starting at the index that corresponds to the target page.

The examples on this page use the following models:

A filter selects a subset of results based on the value(s) of one or more object properties. Realm Database provides a full-featured query engine that you can use to define filters.

Tip
Filter on Related and Embedded Object Properties

To filter a query based on a property of an embedded object or a related object, use dot-notation as if it were in a regular, nested object.

The types in your predicate must match the types of the properties. Avoid comparing ObjectId properties to strings, as Realm Database does not automatically convert strings to ObjectIds.

You can query through a relationship the same way you would access a member of a regular Swift or Objective-C object.

You can query through an inverse relationship the same way you would access a member of a regular Swift or Objective-C object.

Use dot notation to filter or sort a collection of objects based on an embedded object property value:

Note

It is not possible to query embedded objects directly. Instead, access embedded objects through a query for the parent object type.

You can iterate and check the values of a realm map as you would a standard Dictionary:

let realm = try! Realm()
let dogs = realm.objects(Dog.self)
// Find dogs who have favorite parks
let dogsWithFavoriteParks = dogs.where {
$0.favoriteParksByCity.count >= 1
}
for dog in dogsWithFavoriteParks {
// Check if an entry exists
if dog.favoriteParksByCity.keys.contains("Chicago") {
print("\(dog.name) has a favorite park in Chicago")
}
// Iterate over entries
for element in dog.favoriteParksByCity {
print("\(dog.name)'s favorite park in \(element.key) is \(element.value)")
}
}

You can query a MutableSet to check if it contains an element. If you are working with multiple sets, you can check for the intersection of two sets, or check whether one set is a subset of the other set.

let realm = try! Realm()
// Find dogs who have visited New York
let newYorkDogs = realm.objects(Dog.self).where {
$0.citiesVisited.contains("New York")
}
// Get some information about the cities they have visited
for dog in newYorkDogs {
print("Cities \(dog.name) has visited: \(dog.citiesVisited)")
}
// Check whether two dogs have visited some of the same cities.
// Use "intersects" to find out whether the values of the two sets share common elements.
let isInBothCitiesVisited = (dog.citiesVisited.intersects(dog2.citiesVisited))
print("The two dogs have visited some of the same cities: \(isInBothCitiesVisited)")
// Prints "The two dogs have visited some of the same cities: true"
// Or you can check whether a set is a subset of another set. In this example,
// the first dog has visited "New York" and "Toronto", while dog2 has visited both of
// those but also "Toronto" and "Boston".
let isSubset = (dog.citiesVisited.isSubset(of: dog2.citiesVisited))
print("\(dog.name)'s set of cities visited is a subset of \(dog2.name)'s: \(isSubset)")
// Prints "Maui's set of cities visited is a subset of Lita's: true"

When you read an AnyRealmValue property, check the value's type before doing anything with it. The Realm Swift SDK provides an AnyRealmValue enum that iterates through all of the types the AnyRealmValue can store.

let realm = try! Realm()
let dogs = realm.objects(Dog.self)
for dog in dogs {
// Verify the type of the ``AnyRealmProperty`` when attempting to get it. This
// returns an object whose property contains the matched type.
// If you only care about one type, check for that type.
if case let .string(companion) = dog.companion {
print("\(dog.name)'s companion is: \(companion)")
// Prints "Wolfie's companion is: Fluffy the Cat"
}
// Or if you want to do something with multiple types of data
// that could be in the value, switch on the type.
switch dog.companion {
case .string:
print("\(dog.name)'s companion is: \(dog.companion)")
// Prints "Wolfie's companion is: string("Fluffy the Cat")
case .object:
print("\(dog.name)'s companion is: \(dog.companion)")
// Prints "Fido's companion is: object(Dog { name = Spot })"
case .none:
print("\(dog.name) has no companion")
// Prints "Rex has no companion" and "Spot has no companion"
default:
print("\(dog.name)'s companion is another type.")
}
}

You can compare these mixed value types:

  • Numeric: int, bool, float, double, decimal
  • Byte-based: string, binary
  • Time-based: timestamp, objectId

When using the AnyRealmValue mixed data type, keep these things in mind:

  • equals queries match on value and type
  • not equals queries match objects with either different values or different types
  • realm converts comparable numeric properties where possible. For example, in a mixed type field, 1 matches all of 1.0, 1, and true.
  • String properties do not match numeric queries. For example, in a mixed type field, 1 does not match "1". "1" does not match 1, 1.0, or true.

When you use type projection to map unsupported types to supported types, accessing those properties is often based on the persisted type.

When working with projected types, queries operate on the persisted type. However, you can use the mapped types interchangeably with the persisted types in arguments in most cases. The exception is queries on embedded objects.

Tip

Projected types support sorting and aggregates where the persisted type supports them.

let akcClub = realm.objects(Club.self).where {
$0.name == "American Kennel Club"
}.first!
// You can use type-safe expressions to check for equality
XCTAssert(akcClub.url == URL(string: "https://akc.org")!)
let clubs = realm.objects(Club.self)
// You can use the persisted property type in NSPredicate query expressions
let akcByUrl = clubs.filter("url == 'https://akc.org'").first!
XCTAssert(akcByUrl.name == "American Kennel Club")

You can query embedded types on the supported property types within the object using memberwise equality.

Object link properties support equality comparisons, but do not support memberwise comparisons. You can query embedded objects for memberwise equality on all primitive types. You cannot perform memberwise comparison on objects and collections.

Because the schema has no concept of custom type mappings, reading data via any of the dynamic APIs gives the underlying persisted type. Realm does support writing mapped types via a dynamic API, and converts the projected type to the persisted type.

The most common use of the dynamic APIs is migration. You can write projected types during migration, and Realm converts the projected type to the persisted type. However, reading data during a migration gives the underlying persisted type.

A sort operation allows you to configure the order in which Realm Database returns queried objects. You can sort based on one or more properties of the objects in the results collection. Realm Database only guarantees a consistent order of results if you explicitly sort them.

Tip
Sort on Related and Embedded Object Properties

To sort a query based on a property of an embedded object or a related object, use dot-notation as if it were in a regular, nested object.

Note

String sorting and case-insensitive queries are only supported for character sets in 'Latin Basic', 'Latin Supplement', 'Latin Extended A', and 'Latin Extended B' (UTF-8 range 0-591).

You can use Realm's aggregation operators for sophisticated queries against list properties.

Because results are lazily evaluated, you can chain several queries together. Unlike traditional databases, this does not require a separate trip to the database for each successive query.

Example

To get a result set for tan dogs, and tan dogs whose names start with 'B', chain two queries like this:

To query for class projections in a realm, pass the metatype instance YourProjectionName.self to Realm.objects(_:). This returns a Results object representing all of the class projection objects in the realm.

// Retrieve all class projections of the given type `PersonProjection`
let people = realm.objects(PersonProjection.self)
// Use projection data in your view
print(people.first?.firstName)
print(people.first?.homeCity)
print(people.first?.firstFriendsName)
Tip

Don't do derived queries on top of class projection results. Instead, run a query against the Realm object directly and then project the result. If you try to do a derived query on top of class projection results, querying a field with the same name and type as the original object works, but querying a field with a name or type that isn't in the original object fails.

←  CRUD - Create - Swift SDKCRUD - Update - Swift SDK →
Give Feedback
© 2022 MongoDB, Inc.

About

  • Careers
  • Investor Relations
  • Legal Notices
  • Privacy Notices
  • Security Information
  • Trust Center
© 2022 MongoDB, Inc.