diff --git a/docs-release-notes.md b/docs-release-notes.md
index 2574742b6c..9b687617fc 100644
--- a/docs-release-notes.md
+++ b/docs-release-notes.md
@@ -1662,7 +1662,7 @@ Prepare docs for C++ GA release, including:
- Model Data
- Define an Object Model: Update info for C# nullable reference types and nullable-aware context, add a Bluehawked code example
- Data Binding: Move a section about data binding and MVVM from Define an Object Model to Data Binding page
-- React to Changes: Add a callout with info about binding data to the UI and a link to the the Data Binding page
+- React to Changes: Add a callout with info about binding data to the UI and a link to the Data Binding page
## Node.js SDK
diff --git a/examples/dotnet/Examples/Asymmetrics.cs b/examples/dotnet/Examples/Asymmetrics.cs
index b4c404011c..5de1c7649a 100644
--- a/examples/dotnet/Examples/Asymmetrics.cs
+++ b/examples/dotnet/Examples/Asymmetrics.cs
@@ -11,25 +11,26 @@ namespace Examples
{
public partial class Asymmetrics
{
- App app;
- Realms.Sync.User user;
Realm realm;
- const string myRealmAppId = Config.FSAppId;
+ const string myAppId = Config.FSAppId;
[OneTimeSetUp]
public void Setup()
{
- app = App.Create(myRealmAppId);
- user = app.LogInAsync(
+ // :snippet-start: connect-and-authenticate
+ App app = App.Create(myAppId);
+ Realms.Sync.User user = app.LogInAsync(
Credentials.Anonymous()).Result;
-
+ // :snippet-end:
+
+ // :snippet-start: configure-and-open-db
var config = new FlexibleSyncConfiguration(user)
{
Schema = new[] { typeof(Measurement) }
};
-
realm = Realm.GetInstance(config);
+ // :snippet-end:
// You cannot add a subscription for an AsymmetricObject
// This causes a compile-time error:
@@ -40,11 +41,8 @@ public void Setup()
//});
// :uncomment-end:
}
-
- // :snippet-start: asymmetry
- // :remove-start:
[Realms.Explicit]
- // :remove-end:
+ // :snippet-start: define-asymmetric-object
private partial class Measurement : IAsymmetricObject
{
[PrimaryKey, MapTo("_id")]
@@ -52,10 +50,9 @@ private partial class Measurement : IAsymmetricObject
public double Value { get; set; }
public DateTimeOffset Timestamp { get; private set; } = DateTimeOffset.UtcNow;
}
-
- // :remove-start:
+ // :snippet-end:
[Test]
- // :remove-end:
+ // :snippet-start: asymmetry
public void SendMeasurementToRealm()
{
var measurement = new Measurement
diff --git a/.github/workflows/java_local.yml b/examples/java/java_local.yml
similarity index 100%
rename from .github/workflows/java_local.yml
rename to examples/java/java_local.yml
diff --git a/.github/workflows/java_sync.yml b/examples/java/java_sync.yml
similarity index 100%
rename from .github/workflows/java_sync.yml
rename to examples/java/java_sync.yml
diff --git a/examples/java/sync/app/src/androidTest/java/com/mongodb/realm/examples/java/ClientResetTest.java b/examples/java/sync/app/src/androidTest/java/com/mongodb/realm/examples/java/ClientResetTest.java
index e9b9272fbc..c3b529ce44 100644
--- a/examples/java/sync/app/src/androidTest/java/com/mongodb/realm/examples/java/ClientResetTest.java
+++ b/examples/java/sync/app/src/androidTest/java/com/mongodb/realm/examples/java/ClientResetTest.java
@@ -238,7 +238,7 @@ public void handleManualReset(App app, SyncSession session, ClientResetRequiredE
Log.w("EXAMPLE", "Opened a fresh instance of the realm.");
- // Open the the realm backup -- as a dynamic realm
+ // Open the realm backup -- as a dynamic realm
// (no formal schema; access all data through field lookups)
DynamicRealm backupRealm = DynamicRealm.getInstance(error.getBackupRealmConfiguration());
Log.w("EXAMPLE", "Opened the backup realm.");
diff --git a/examples/java/sync/app/src/androidTest/java/com/mongodb/realm/examples/kotlin/ClientResetTest.kt b/examples/java/sync/app/src/androidTest/java/com/mongodb/realm/examples/kotlin/ClientResetTest.kt
index 7ce7d1e76c..99166eca80 100644
--- a/examples/java/sync/app/src/androidTest/java/com/mongodb/realm/examples/kotlin/ClientResetTest.kt
+++ b/examples/java/sync/app/src/androidTest/java/com/mongodb/realm/examples/kotlin/ClientResetTest.kt
@@ -265,7 +265,7 @@ class ClientResetTest : RealmTest() {
}
Log.w("EXAMPLE", "Opened a fresh instance of the realm.")
- // Open the the realm backup -- as a dynamic realm
+ // Open the realm backup -- as a dynamic realm
// (no formal schema; access all data through field lookups)
val backupRealm =
DynamicRealm.getInstance(error.backupRealmConfiguration)
diff --git a/examples/node/legacy/Examples/realm-query-language.js b/examples/node/legacy/Examples/realm-query-language.js
index 305e8ccab2..e7a1e8eb41 100644
--- a/examples/node/legacy/Examples/realm-query-language.js
+++ b/examples/node/legacy/Examples/realm-query-language.js
@@ -1,9 +1,12 @@
import Realm, { BSON } from "realm";
import { ItemModel, ProjectModel } from "./schemas/rql-data-models";
-// Tests for new RQL operators or updates should be placed in the new
-// file compatible with JSv12 and later, located at
-// examples/node/v12/__tests__/realm-query-language.test.js
+/*
+ DON'T UPDATE THIS FILE. DEPRECATED IN FAVOR OF V12 TESTS.
+ Tests for new RQL operators or updates should be placed in the new
+ files compatible with JSv12 and later, located at:
+ examples/node/v12/__tests__/realm-query-language.test.js/ts
+*/
describe("Realm Query Language Reference", () => {
let realm;
@@ -148,7 +151,7 @@ describe("Realm Query Language Reference", () => {
const progressMinutesRange = items.filtered(
// :remove-end:
- // Find to-do items within a certain time range by finding items
+ // Find to-do items within a certain time range by finding items
// where the progressMinutes property is between two numbers.
"progressMinutes BETWEEN { $0 , $1 }", 30, 60
// :remove-start:
@@ -406,7 +409,7 @@ describe("Realm Query Language Reference", () => {
);
expect(shallowResultLinkingObjects.length).toBe(1);
expect(shallowResultLinkingObjects[0].name).toBe("Get coffee");
-
+
const shallowResultAtLinks = realm.objects("Item").filtered(
// :remove-end:
// Find items that are not referenced by any project
diff --git a/examples/node/legacy/Examples/rql-data-models.js b/examples/node/legacy/Examples/rql-data-models.js
index 8277722b6e..4cc90a4b90 100644
--- a/examples/node/legacy/Examples/rql-data-models.js
+++ b/examples/node/legacy/Examples/rql-data-models.js
@@ -1,6 +1,13 @@
import Realm from "realm";
import { ItemModel, ProjectModel } from "./schemas/rql-data-models";
+/*
+ DON'T UPDATE THIS FILE. DEPRECATED IN FAVOR OF V12 TESTS.
+ Updates should be placed in the new files compatible with
+ JSv12 and later, located at:
+ examples/node/v12/__tests__/rql-data-models.test.js/ts
+*/
+
describe("test models", () => {
let realm;
const config = {
diff --git a/examples/node/legacy/Examples/schemas/rql-data-models.js b/examples/node/legacy/Examples/schemas/rql-data-models.js
index 0e2cd9a02f..9c136650e5 100644
--- a/examples/node/legacy/Examples/schemas/rql-data-models.js
+++ b/examples/node/legacy/Examples/schemas/rql-data-models.js
@@ -1,3 +1,9 @@
+/*
+ DON'T UPDATE THIS FILE. DEPRECATED IN FAVOR OF V12 MODELS.
+ Updates should be placed in the new file compatible with
+ JSv12 and later, located at:
+ examples/node/v12/__tests__/models/rql-data-models.ts
+*/
// :snippet-start: rql-data-models
const ItemModel = {
name: "Item",
diff --git a/examples/node/v12/__tests__/models/rql-data-models.js b/examples/node/v12/__tests__/models/rql-data-models.js
new file mode 100644
index 0000000000..01f5d485f8
--- /dev/null
+++ b/examples/node/v12/__tests__/models/rql-data-models.js
@@ -0,0 +1,54 @@
+// Models for embedded objects & dot notation queries
+const Address = {
+ name: "Address",
+ embedded: true,
+ properties: {
+ name: "string",
+ street: "string",
+ zipcode: "int",
+ },
+};
+
+const Office = {
+ name: "Office",
+ properties: {
+ name: "string",
+ address: "Address",
+ },
+};
+
+// :snippet-start: rql-data-models
+const Item = {
+ name: "Item",
+ properties: {
+ _id: "objectId",
+ name: { type: "string", indexed: "full-text" },
+ isComplete: { type: "bool", default: false },
+ assignee: "string?",
+ priority: { type: "int", default: 0 },
+ progressMinutes: { type: "int", default: 0 },
+ projects: {
+ type: "linkingObjects",
+ objectType: "Project",
+ property: "items",
+ },
+ },
+ primaryKey: "_id",
+};
+
+const Project = {
+ name: "Project",
+ properties: {
+ _id: "objectId",
+ name: "string",
+ items: "Item[]",
+ quota: "int?",
+ comments: "string?{}",
+ projectLocation: "Office?",
+ additionalInfo: "mixed",
+ },
+ primaryKey: "_id",
+};
+// :snippet-end:
+
+export { Address, Item, Office, Project };
diff --git a/examples/node/v12/__tests__/models/rql-data-models.ts b/examples/node/v12/__tests__/models/rql-data-models.ts
index 210aaddcdc..776e1d1b94 100644
--- a/examples/node/v12/__tests__/models/rql-data-models.ts
+++ b/examples/node/v12/__tests__/models/rql-data-models.ts
@@ -1,5 +1,36 @@
import Realm, { BSON, ObjectSchema } from "realm";
+// Models for embedded objects & dot notation queries
+export class Address extends Realm.Object
{
+ name!: string;
+ street!: string;
+ zipcode!: number;
+
+ static schema: ObjectSchema = {
+ name: "Address",
+ embedded: true,
+ properties: {
+ name: "string",
+ street: "string",
+ zipcode: "int",
+ },
+ };
+}
+
+export class Office extends Realm.Object {
+ name!: string;
+ address!: Address;
+
+ static schema: ObjectSchema = {
+ name: "Office",
+ properties: {
+ name: "string",
+ address: "Address",
+ },
+ };
+}
+
+// :snippet-start: rql-data-models
export class Item extends Realm.Object- {
_id!: BSON.ObjectId;
name!: string;
@@ -12,17 +43,11 @@ export class Item extends Realm.Object
- {
name: "Item",
properties: {
_id: "objectId",
- name: { type:"string", indexed: "full-text" },
+ name: { type: "string", indexed: "full-text" },
isComplete: { type: "bool", default: false },
assignee: "string?",
- priority: {
- type: "int",
- default: 0,
- },
- progressMinutes: {
- type: "int",
- default: 0,
- },
+ priority: { type: "int", default: 0 },
+ progressMinutes: { type: "int", default: 0 },
projects: {
type: "linkingObjects",
objectType: "Project",
@@ -32,12 +57,14 @@ export class Item extends Realm.Object
- {
primaryKey: "_id",
};
}
-
export class Project extends Realm.Object {
_id!: BSON.ObjectId;
name!: string;
- items!: Realm.List
-
- quota?: number
+ items!: Realm.List
- ;
+ quota?: number;
+ comments?: Realm.Dictionary;
+ projectLocation?: Office;
+ additionalInfo!: Realm.Mixed;
static schema: ObjectSchema = {
name: "Project",
@@ -46,7 +73,11 @@ export class Project extends Realm.Object {
name: "string",
items: "Item[]",
quota: "int?",
+ comments: "string?{}",
+ projectLocation: "Office?",
+ additionalInfo: "mixed",
},
primaryKey: "_id",
- }
-};
+ };
+}
+// :snippet-end:
diff --git a/examples/node/v12/__tests__/realm-query-language.test.js b/examples/node/v12/__tests__/realm-query-language.test.js
index 81a11913c9..a265de2685 100644
--- a/examples/node/v12/__tests__/realm-query-language.test.js
+++ b/examples/node/v12/__tests__/realm-query-language.test.js
@@ -1,22 +1,51 @@
import Realm, { BSON } from "realm";
-import { Item, Project } from "./models/rql-data-models.ts";
+import { Address, Item, Office, Project } from "./models/rql-data-models.ts";
+import { describe, expect } from "@jest/globals";
describe("Realm Query Language Reference", () => {
let realm;
+ const config = { schema: [Project, Item, Address, Office] };
+
beforeEach(async () => {
- realm = await Realm.open({
- schema: [Project, Item],
- deleteRealmIfMigrationNeeded: true,
- });
+ // Before each test, open the realm and create project & item objects:
+ // 2 branches w/ 2 addresses; 3 projects; and
+ // 6 items (one used in 2 projects, another in 0 projects)
+ realm = await Realm.open(config);
- // populate test objects
+ const mainBranch = {
+ name: "Main Branch",
+ address: {
+ name: "Main Branch",
+ street: "999 Big Boulevard",
+ zipcode: 10019,
+ },
+ };
+ const austinBranch = {
+ name: "Austin Branch",
+ address: {
+ name: "Austin Branch",
+ street: "123 Main Ave",
+ zipcode: 10019,
+ },
+ };
realm.write(() => {
- realm.create("Project", {
- _id: new Realm.BSON.ObjectId(),
- name: "New Project",
+ realm.create(Item, {
+ // Unassigned incomplete item not in any project
+ _id: new BSON.ObjectId("631a073c833a34ade21db2b2"),
+ name: "Write tests",
+ isComplete: false,
+ assignee: null,
+ priority: 10,
+ progressMinutes: 0,
+ });
+
+ realm.create(Project, {
+ _id: new BSON.ObjectId(),
+ name: "Example Project with Items",
items: [
{
- _id: new Realm.BSON.ObjectId(),
+ // Alex incomplete item
+ _id: new BSON.ObjectId("631a072f75120729dc9223d9"),
name: "Write tests",
isComplete: false,
assignee: "Alex",
@@ -24,73 +53,96 @@ describe("Realm Query Language Reference", () => {
progressMinutes: 125,
},
{
- _id: new Realm.BSON.ObjectId(),
+ // Ali incomplete item
+ _id: new BSON.ObjectId("631a0737c98f89f5b81cd24d"),
name: "Run tests",
isComplete: false,
assignee: "Ali",
priority: 9,
progressMinutes: 10,
},
- {
- _id: new Realm.BSON.ObjectId(),
- name: "Bluehawk Tests",
- isComplete: false,
- assignee: null,
- priority: 10,
- progressMinutes: 55,
- },
],
+ quota: 1, // doesn't meet quota
+ comments: { status: "Behind schedule", projectNumber: "70150" },
+ projectLocation: mainBranch,
+ // Mixed property is of type dictionary of mixed values
+ // (date, list of strings, bool, and int)
+ additionalInfo: {
+ startDate: new Date("2021-01-01"),
+ customerContacts: ["Alice", "Bob"],
+ recurringCustomer: true,
+ budget: 10000,
+ },
});
- const proj1 = realm.create("Project", {
- _id: new Realm.BSON.ObjectId(),
- name: "Project with High Quota",
- quota: 12,
+ const project = realm.create(Project, {
+ _id: new BSON.ObjectId(),
+ name: "Project that Meets Quota",
items: [
{
- _id: new Realm.BSON.ObjectId(),
- name: "Create a ticket",
+ // Complete item used in 2 projects
+ _id: new BSON.ObjectId(),
+ name: "Approve project plan",
isComplete: true,
- assignee: "Nick",
- priority: 2,
- progressMinutes: 8,
+ assignee: "Dachary",
+ priority: 1,
+ progressMinutes: 0,
},
{
- _id: new Realm.BSON.ObjectId(),
- name: "Schedule a meeting",
+ // Complete high-priority item
+ _id: new BSON.ObjectId(),
+ name: "Create a ticket",
isComplete: true,
assignee: "Chris",
priority: 9,
progressMinutes: 10,
},
{
- _id: new Realm.BSON.ObjectId(),
- name: "Get coffee",
+ // Incomplete high-priority item
+ _id: new BSON.ObjectId(),
+ name: "Demo template app",
isComplete: false,
assignee: "Dachary",
priority: 11,
progressMinutes: 3,
},
],
+ quota: 1, // meets quota
+ comments: { status: "Ahead of schedule", projectNumber: "70187" },
+ projectLocation: mainBranch,
+ // Mixed property is of type string
+ additionalInfo: "Customer is a VIP.",
});
- const proj2 = realm.create("Project", {
- _id: new Realm.BSON.ObjectId(),
- name: "Another project",
- items: [proj1.items[2]],
- });
-
- realm.create("Item", {
- _id: new Realm.BSON.ObjectId(),
- name: "Assign me to a project",
- isComplete: false,
- assignee: "Nick",
- priority: 2,
- progressMinutes: 0,
+ realm.create(Project, {
+ _id: new BSON.ObjectId(),
+ name: "Project in Austin",
+ items: [
+ project.items[0],
+ {
+ // Incomplete high-priority item assigned to `nil` type
+ _id: new BSON.ObjectId(),
+ name: "Lead offsite workshop",
+ isComplete: false,
+ assignee: "nil",
+ priority: 10,
+ progressMinutes: 0,
+ },
+ ],
+ quota: 11, // doesn't meet quota
+ comments: { status: "On track", projectNumber: "N/A" },
+ projectLocation: austinBranch,
+ // Mixed property is of type list, containing string and nested
+ // dictionary of mixed values (date, boolean, and int)
+ additionalInfo: [
+ "Customer is difficult to work with.",
+ {
+ startDate: new Date("2021-03-01"),
+ recurringCustomer: false,
+ budget: 10000,
+ },
+ ],
});
});
-
- expect(realm.objects("Project")[0].name).toBe("New Project");
- expect(realm.objects("Item")[0].name).toBe("Write tests");
});
afterEach(() => {
@@ -100,37 +152,807 @@ describe("Realm Query Language Reference", () => {
realm.deleteAll();
});
realm.close();
+ expect(realm.isClosed).toBe(true);
}
});
- test("full-text search", () => {
+ afterAll(() => {
+ Realm.deleteFile(config);
+ });
+
+ test("Can open realm and create objects", async () => {
+ expect(realm.isClosed).toBe(false);
+ expect(realm.objects(Project)[0].name).toBe("Example Project with Items");
+ expect(realm.objects(Item)[0].name).toBe("Write tests");
+ });
+
+ test("Simple query", () => {
+ // NOTE: snippet used on Node.js Query Data page, not RQL page
+ // [snippet-start] simple-query
const items = realm.objects(Item);
+ // Get all items where 'priority' property is 7 or more.
+ const importantItems = items.filtered("priority >= $0", 7);
+ // [snippet-end]
+ expect(importantItems.length).toEqual(5);
+ });
- const itemsWithWrite = items.filtered(
- // :snippet-start: rql-fts
- // Filter for items with 'write' in the name
- "name TEXT $0", "write"
+ describe("Basic syntax", () => {
+ test("Expression query", () => {
+ const items = realm.objects(Item);
+ // [snippet-start] predicate
+ const expression = "priority == 1";
+ // Property Name: priority
+ // Operator: ==
+ // Value: 1
+ // [snippet-end]
+ expect(items.filtered(expression).length).toBe(1);
+ });
+ test("Serialized query", () => {
+ const items = realm.objects(Item);
+ const query = items.filtered(
+ // [snippet-start] serialized-query
+ "progressMinutes > 1 AND assignee == 'Ali'"
+ // [snippet-end]
+ );
+ expect(query.length).toBe(1);
+ });
+ test("Parameterized query", () => {
+ const items = realm.objects(Item);
+ // prettier-ignore
+ const substitution = items.filtered(
+ // [snippet-start] parameterized-query
+ // Include one parameter with `$0`.
+ "progressMinutes > 1 AND assignee == $0", "Ali"
- // :remove-start:
+ // [remove-start]
);
+ expect(substitution.length).toBe(1);
+ });
- const itemsWithWriteNotTest = items.filtered(
- // :remove-end:
- // Find items with 'write' but not 'tests' using '-'
- "name TEXT $0", "write -tests"
+ test("Multiple parameterized query", () => {
+ const items = realm.objects(Item);
+ // prettier-ignore
+ const substitution = items.filtered(
+ // [remove-end]
+ // Include multiple parameters using ascending integers, starting at`$0`.
+ "progressMinutes > $0 AND assignee == $1", 1, "Alex"
+ // [snippet-end]
+ );
+ expect(substitution.length).toBe(1);
+ });
- // :remove-start:
+ test("Dot notation", () => {
+ const address = realm.objects(Project);
+ const nestedItem = address.filtered(
+ // [snippet-start] dot-notation
+ // Find projects whose `items` list property contains an item with a specific name.
+ "items[0].name == 'Approve project plan'"
+ // [snippet-end]
);
+ const nestedZipcode = address.filtered(
+ // [snippet-start] deep-dot-notation
+ // Find projects whose `projectLocation` property contains an embedded Address object with a specific zip code.
+ "projectLocation.address.zipcode == 10019"
+ // [snippet-end]
+ );
+ expect(nestedItem.length).toBe(2);
+ expect(nestedZipcode.length).toBe(3);
+ });
+ });
- const itemsStartingWithWri = items.filtered(
- // :remove-end:
- // Find items starting with 'wri-' using '*'
- "name TEXT $0", "wri*"
- // :snippet-end:
+ // prettier-ignore
+ test("Comparison operators", () => {
+ const items = realm.objects(Item);
+
+ const highPriorityItems = items.filtered(
+ // [snippet-start] comparison-operators
+ // Compare `priority` values against a threshold value.
+ "priority > $0", 5
+
+ // [remove-start]
+ );
+ expect(highPriorityItems.length).toBe(5);
+
+ const unassignedItems = items.filtered(
+ // [remove-end]
+ // Compare `assignee` values to `null` value.
+ "assignee == $0", null
+
+ // [remove-start]
+ );
+ expect(unassignedItems.length).toBe(1);
+
+ const progressMinutesRange = items.filtered(
+ // [remove-end]
+ // Compare `priority` values against an inclusive range of values.
+ "priority BETWEEN { $0 , $1 }", 1, 5
+
+ // [remove-start]
+ );
+ expect(progressMinutesRange.length).toBe(2);
+
+ const progressMinutesIn = items.filtered(
+ // [remove-end]
+ // Compare `progressMinutes` values against any of the listed values.
+ "progressMinutes IN { $0, $1, $2 }", 10, 30, 60
+ // [snippet-end]
+ );
+ });
+
+ // prettier-ignore
+ test("Logical operators", () => {
+ const items = realm.objects(Item);
+ const aliComplete = items.filtered(
+ // [snippet-start] logical-operators
+ // Find all items assigned to Ali AND marked completed.
+ "assignee == $0 AND isComplete == $1", "Ali", true
+
+ // [remove-start]
+ );
+ const alexOrAli = items.filtered(
+ // [remove-end]
+ // Find all items assigned to Alex OR to Ali.
+ "assignee == $0 OR assignee == $1", "Alex", "Ali"
+ // [snippet-end]
+ );
+ expect(aliComplete.length).toBe(0);
+ expect(alexOrAli.length).toBe(2);
+ });
+
+ describe("Arithmetic operators", () => {
+ test("Basic arithmetic", () => {
+ const items = realm.objects(Item);
+ const basicMath = items.filtered(
+ // [snippet-start] basic-arithmetic
+ // Evaluate against an item's `priority` property value:
+ "2 * priority > 6" // resolves to `priority > 3`
+
+ // [remove-start]
);
+ const lessBasicMath = items.filtered(
+ // [remove-end]
+ "priority >= 2 * (2 - 1) + 2" // resolves to `priority >= 4`
- expect(itemsWithWrite.length).toBe(1)
+ // [remove-start]
+ );
+ expect(basicMath.length).toBe(6);
+ expect(lessBasicMath.length).toBe(6);
+ });
+
+ test("Arithmetic with object properties", () => {
+ const items = realm.objects(Item);
+ const mathWithObjProps = items.filtered(
+ // [remove-end]
+ // Evaluate against multiple object property values:
+ "progressMinutes * priority == 90"
+ // [snippet-end]
+ );
+ expect(mathWithObjProps.length).toBe(2);
+ });
+ });
+
+ // prettier-ignore
+ describe("Type-specific operators", () => {
+ test("String operators", () => {
+ const projects = realm.objects(Project);
+ const startWithE = projects.filtered(
+ // [snippet-start] string-operators
+ // Find projects whose name starts with 'E' or 'e' (case-insensitive).
+ "name BEGINSWITH[c] $0", "E"
+
+ // [remove-start]
+ );
+ expect(startWithE.length).toBe(1);
+
+ const containIe = projects.filtered(
+ // [remove-end]
+ // Find projects whose name contains 'ie' (case-sensitive).
+ "name CONTAINS $0", "ie"
+
+ // [remove-start]
+ );
+ expect(containIe.length).toBe(0);
+ });
+
+ test("String comparisons", () => {
+ const projects = realm.objects(Project);
+ const items = realm.objects(Item);
+ // prettier-ignore
+ const assigneeBetween = items.filtered(
+ // [remove-end]
+ // Find items where the assignee name is lexicographically between 'Ali' and 'Chris' (case-sensitive).
+ "assignee BETWEEN { $0 , $1 }", "Ali", "Chris"
+
+ // [remove-start]
+ );
+ // prettier-ignore
+ const compareStreet = projects.filtered(
+ // [remove-end]
+ // Find projects where the street address is lexicographically greater than '123 Main St' (case-sensitive).
+ "projectLocation.address.street > $0", "123 Main St"
+ // [snippet-end]
+ );
+ expect(compareStreet.length).toBe(2);
+ expect(assigneeBetween.length).toBe(2);
+ });
+ });
+
+ // prettier-ignore
+ test("Aggregate queries", () => {
+ const projects = realm.objects(Project);
+ // [snippet-start] aggregate-operators
+ var priorityNum = 5;
+
+ // [remove-start]
+ const averageItemPriorityAbove5 = projects.filtered(
+ // [remove-end]
+ // Find projects with average item `priority` above 5.
+ "items.@avg.priority > $0", priorityNum
+
+ // [remove-start]
+ );
+ expect(averageItemPriorityAbove5.length).toBe(3);
+
+ const allItemsLowerPriority = projects.filtered(
+ // [remove-end]
+ // Find projects where maximum `priority` of all items is 5.
+ "items.@max.priority < $0", priorityNum
+
+ // [remove-start]
+ );
+ expect(allItemsLowerPriority.length).toBe(0);
+
+ const allItemsHighPriority = projects.filtered(
+ // [remove-end]
+ // Find projects where minimum `priority` of all items is 5.
+ "items.@min.priority > $0", priorityNum
+
+ // [remove-start]
+ );
+ expect(allItemsHighPriority.length).toBe(0);
+
+ const moreThan5Items = projects.filtered(
+ // [remove-end]
+
+ // Find projects with more than 5 items.
+ "items.@count > $0", 5
+
+ // [remove-start]
+ );
+ expect(moreThan5Items.length).toBe(0);
+
+ const longRunningProjects = projects.filtered(
+ // [remove-end]
+ // Find projects where the sum total value of `progressMinutes`
+ // across all items is greater than 100.
+ "items.@sum.progressMinutes > $0", 100
+ // [snippet-end]
+ );
+ expect(longRunningProjects.length).toBe(1);
+ });
+
+ describe("Collection operators", () => {
+ test("Collection queries", () => {
+ const projects = realm.objects(Project);
+ // prettier-ignore
+ const noCompleteItems = projects.filtered(
+ // [snippet-start] set-operators
+ // Find projects with no complete items.
+ "NONE items.isComplete == $0", true
+
+ // [remove-start]
+ );
+ // prettier-ignore
+ const anyTopPriorityItems = projects.filtered(
+ // [remove-end]
+ // Find projects that contain any item with priority 10.
+ "items.priority == $0", 10 // (ANY operator is implied.)
+
+ // [remove-start]
+ );
+ // prettier-ignore
+ const allItemsCompleted = projects.filtered(
+ // [remove-end]
+ // Find projects that only contain completed items.
+ "ALL items.isComplete == $0", true
+
+ // [remove-start]
+ );
+ // prettier-ignore
+ const assignedToAlexOrAli = projects.filtered(
+ // [remove-end]
+ // Find projects with at least one item assigned to either Alex or Ali.
+ "ANY items.assignee IN { $0 , $1 }", "Alex", "Ali"
+
+ // [remove-start]
+ );
+ // prettier-ignore
+ const notAssignedToAlexOrAli = projects.filtered(
+ // [remove-end]
+ // Find projects with no items assigned to either Alex or Ali.
+ "NONE items.assignee IN { $0 , $1 }", "Alex", "Ali"
+ // [snippet-end]
+ );
+ expect(noCompleteItems.length).toBe(1);
+ expect(anyTopPriorityItems.length).toBe(1);
+ expect(allItemsCompleted.length).toBe(0);
+ expect(assignedToAlexOrAli.length).toBe(1);
+ expect(notAssignedToAlexOrAli.length).toBe(2);
+ });
+
+ test("List comparisons", () => {
+ realm.write(() => {});
+ const projects = realm.objects(Project);
+ const items = realm.objects(Item);
+
+ const collectionQuery = projects.filtered(
+ // [snippet-start] list-comparisons-collection
+ // Find an item with the specified ObjectId value in the `items` collection.
+ "oid(631a072f75120729dc9223d9) IN items._id"
+ // [snippet-end]
+ );
+ const staticQuery = items.filtered(
+ // [snippet-start] list-comparisons-static
+ // Find items with a priority value matching any value in the static list.
+ "priority IN {0, 1, 2}"
+ // [snippet-end]
+ );
+ // [snippet-start] list-comparisons-parameterized
+ const ids = [
+ new BSON.ObjectId("631a072f75120729dc9223d9"),
+ new BSON.ObjectId("631a0737c98f89f5b81cd24d"),
+ new BSON.ObjectId("631a073c833a34ade21db2b2"),
+ ];
+ // Find items with an ObjectId value matching any value in the parameterized list.
+ const parameterizedQuery = realm.objects(Item).filtered("_id IN $0", ids);
+ // [snippet-end]
+
+ // prettier-ignore
+ const anyOperator = items.filtered(
+ // [snippet-start] equivalent-lists-any-operator
+ "assignee == ANY { $0, $1 }", "Alex", "Ali"
+
+ // [remove-start]
+ );
+ // prettier-ignore
+ const equivalentAnyOperator = items.filtered(
+ // [remove-end]
+ "assignee == { $0, $1 }", "Alex", "Ali" // Equivalent (ANY is implied.)
+ // [snippet-end]
+ );
+
+ expect(anyOperator.length).toBe(2);
+ expect(equivalentAnyOperator.length).toBe(2);
+ expect(collectionQuery.length).toBe(1);
+ expect(staticQuery.length).toBe(1);
+ expect(parameterizedQuery.length).toBe(3);
+ });
+
+ test("Sort, distinct, and limit results", () => {
+ const items = realm.objects(Item);
+
+ const sortedItems = items.filtered(
+ // [snippet-start] sort-distinct-limit
+ // Find incomplete items, sort by `priority` in descending order, then
+ // sort equal `priority` values by `progressMinutes` in ascending order.
+ "isComplete == false SORT(priority DESC, progressMinutes ASC)"
+
+ // [remove-start]
+ );
+ expect(sortedItems[0].name).toBe("Demo template app");
+ const distinctItems = items.filtered(
+ // [remove-end]
+ // Find high priority items, then remove from the results any items
+ // with duplicate values for both `name` AND `assignee` properties.
+ "priority >= 5 DISTINCT(name, assignee)"
+
+ // [remove-start]
+ );
+ expect(distinctItems.length).toBe(6);
+ const limitItems = items.filtered(
+ // [remove-end]
+ // Find in-progress items, then return the first 10 results.
+ "progressMinutes > 0 && isComplete != true LIMIT(10)"
+ // [snippet-end]
+ );
+ expect(limitItems[0].name).toBe("Write tests");
+
+ const sortFirst = items.filtered(
+ // [snippet-start] sort-distinct-limit-order-matters
+ // 1. Sorts by highest priority.
+ // 2. Returns the first item.
+ // 3. Remove duplicate names (N/A because a single item is always considered distinct).
+ "assignee == null SORT(priority ASC) LIMIT(1) DISTINCT(name)"
+
+ // [remove-start]
+ );
+ const limitLast = items.filtered(
+ // [remove-end]
+ // 1. Removes any duplicates by name.
+ // 2. Sorts by highest priority.
+ // 3. Returns the first item.
+ "assignee == null DISTINCT(name) SORT(priority ASC) LIMIT(1)"
+ // [snippet-end]
+ );
+ });
+
+ test("Subquery query", () => {
+ const projects = realm.objects(Project);
+ const subquery = projects.filtered(
+ // [snippet-start] subquery
+ // Find projects with incomplete items with 'Demo' in the name.
+ "SUBQUERY(items, $item, $item.isComplete == false AND $item.name CONTAINS[c] 'Demo').@count > 0"
+
+ // [remove-start]
+ );
+ expect(subquery.length).toBe(1);
+ expect(subquery[0].name).toBe("Project that Meets Quota");
+
+ const subquery2 = projects.filtered(
+ // [remove-end]
+ // Find projects where the number of completed items is greater than or equal to the project's `quota` property.
+ "SUBQUERY(items, $item, $item.isComplete == true).@count >= quota"
+ // [snippet-end]
+ );
+
+ expect(subquery2.length).toBe(1);
+ expect(subquery2[0].name).toBe("Project that Meets Quota");
+ });
+
+ // prettier-ignore
+ test("Dictionary operators", () => {
+ const dictionaries = realm.objects(Project);
+ const statusKey = dictionaries.filtered(
+ // [snippet-start] dictionary-operators
+ // Find projects whose `comments` dictionary property have a key of 'status'.
+ "comments.@keys == $0", "status"
+
+ // [remove-start]
+ );
+ const statusOnTrack = dictionaries.filtered(
+ // [remove-end]
+ // Find projects whose `comments` dictionary property have a 'status' key with a value that ends in 'track'.
+ "comments['status'] LIKE $0", "*track"
+
+ // [remove-start]
+ );
+ const numItemsInDict = dictionaries.filtered(
+ // [remove-end]
+ // Find projects whose `comments` dictionary property have more than one key-value pair.
+ "comments.@count > $0", 1
+
+ // [remove-start]
+ );
+
+ const allString = dictionaries.filtered(
+ // [remove-end]
+ // Find projects whose `comments` dictionary property contains only values of type 'string'.
+ "ALL comments.@type == 'string'"
+
+ // [remove-start]
+ );
+
+ const noInts = dictionaries.filtered(
+ // [remove-end]
+ // Find projects whose `comments` dictionary property contains no values of type 'int'.
+ "NONE comments.@type == 'int'"
+ // [snippet-end]
+ );
+ expect(statusKey.length).toBe(3);
+ expect(statusOnTrack.length).toBe(1);
+ expect(numItemsInDict.length).toBe(3);
+ expect(allString.length).toBe(3);
+ expect(noInts.length).toBe(3);
+ });
+ });
+
+ describe("Backlinks queries", () => {
+ test("Backlinks query @links", () => {
+ const atLinksResult = realm.objects(Item).filtered(
+ // [snippet-start] backlinks-atLinks
+ // Find items that belong to a project with a quota less than 10
+ // (using '@links..').
+ "@links.Project.items.quota < 10"
+ // [snippet-end]
+ );
+ expect(atLinksResult.length).toBe(5);
+
+ const linkingObjectsResult = realm.objects(Item).filtered(
+ // [snippet-start] backlinks-linkingObjects
+ // Find items that belong to a project with a quota greater than 10 through the Item object's `projects` property
+ // (using 'LinkingObjects').
+ "projects.quota > 10"
+ // [snippet-end]
+ );
+ expect(linkingObjectsResult.length).toBe(2);
+ });
+
+ test("Backlinks collection operators", () => {
+ const anyResult = realm.objects(Item).filtered(
+ // [snippet-start] backlinks-collection-operators
+ // Find items where no project that references the item has a quota greater than 10.
+ "NONE @links.Project.items.quota > 10"
+
+ // [remove-start]
+ );
+ expect(anyResult.length).toBe(5);
+
+ const allResult = realm.objects(Item).filtered(
+ // [remove-end]
+ // Find items where all projects that reference the item have a quota less than 5.
+ "ALL @links.Project.items.quota < 5"
+
+ // [remove-start]
+ );
+ expect(allResult.length).toBe(5);
+ });
+
+ test("Backlinks aggregate operators", () => {
+ const shallowResultLinkingObjects = realm.objects(Item).filtered(
+ // [remove-end]
+ // Find items that are referenced by multiple projects.
+ "projects.@count > 1"
+
+ // [remove-start]
+ );
+ expect(shallowResultLinkingObjects.length).toBe(1);
+ expect(shallowResultLinkingObjects[0].name).toBe("Approve project plan");
+
+ const shallowResultAtLinks = realm.objects(Item).filtered(
+ // [remove-end]
+ // Find items that are not referenced by any project.
+ "@links.Project.items.@count == 0"
+
+ // [remove-start]
+ );
+ expect(shallowResultAtLinks.length).toBe(1);
+ expect(shallowResultAtLinks[0].name).toBe("Write tests");
+
+ const deepResultAtLinks = realm.objects(Item).filtered(
+ // [remove-end]
+ // Find items that belong to a project where the average item has been worked on for at least 10 minutes
+ "@links.Project.items.items.@avg.progressMinutes > 10"
+
+ // [remove-start]
+ );
+ expect(deepResultAtLinks.length).toBe(2);
+ expect(deepResultAtLinks[0].name).toBe("Write tests");
+ });
+
+ test("Count all backlinks (@links.@count)", () => {
+ const result = realm.objects(Item).filtered(
+ // [remove-end]
+ // Find items that are not referenced by another object of any type (backlink count is 0).
+ "@links.@count == 0"
+ // [snippet-end]
+ );
+ expect(result.length).toBe(1);
+ expect(result[0].name).toBe("Write tests");
+ });
+ });
+
+ describe("Type operators", () => {
+ test("Type operator", () => {
+ const projects = realm.objects(Project);
+ const mixedString = projects.filtered(
+ // [snippet-start] type-operator
+ // Find projects with an `additionalInfo` property of string type.
+ "additionalInfo.@type == 'string'"
+
+ // [remove-start]
+ );
+ const mixedCollection = projects.filtered(
+ // [remove-end]
+ // Find projects with an `additionalInfo` property of
+ // `collection` type, which matches list or dictionary types.
+ "additionalInfo.@type == 'collection'"
+
+ // [remove-start]
+ );
+ const mixedBool = projects.filtered(
+ // [remove-end]
+ // Find projects with an `additionalInfo` property of list type, where any list element is of type 'bool'.
+ "additionalInfo[*].@type == 'bool'"
+ // [snippet-end]
+ );
+ expect(mixedString.length).toBe(1);
+ expect(mixedBool.length).toBe(1);
+ });
+ });
+
+ test("Nil type", () => {
+ const items = realm.objects(Item);
+ const res = items.filtered(
+ // [snippet-start] nil-type
+ "assignee == nil"
+ // [remove-start]
+ );
+ // prettier-ignore
+ const res2 = realm.objects(Item).filtered(
+ // [remove-end]
+ "assignee == $0", null // 'null' maps to the SDK language's null pointer
+ // [snippet-end]
+ );
+ expect(res.length).toBe(1);
+ expect(res2.length).toBe(1);
+ });
+
+ describe("ObjectId and UUID queries", () => {
+ // Uses a test-specific schema with id types
+ const OidUuid = {
+ name: "OidUuid",
+ properties: { id: "uuid", _id: "objectId" },
+ };
+
+ let realm;
+ const path = "oidUuid.realm";
+
+ const oidValueString = "6001c033600510df3bbfd864";
+ const uuid1String = "d1b186e1-e9e0-4768-a1a7-c492519d47ee";
+ const oidValue = new BSON.ObjectId(oidValueString);
+ const uuidValue = new BSON.UUID(uuid1String);
+
+ // Add, then delete objects for this test
+ beforeEach(async () => {
+ realm = await Realm.open({ schema: [OidUuid], path });
+ const obj1 = {
+ _id: oidValue,
+ id: uuidValue,
+ };
+ const obj2 = {
+ _id: new BSON.ObjectId(),
+ id: new BSON.UUID(),
+ };
+ realm.write(() => {
+ realm.create("OidUuid", obj1);
+ realm.create("OidUuid", obj2);
+ });
+ });
+
+ afterEach(() => {
+ realm.close();
+ Realm.deleteFile({ path });
+ });
+
+ test("ObjectId query", () => {
+ const oidUuids = realm.objects("OidUuid");
+ // prettier-ignore
+ const oidStringLiteral = oidUuids.filtered(
+ // [snippet-start] oid
+ // Find an item whose `_id` matches the ObjectID value passed to 'oid()'.
+ "_id == oid(6001c033600510df3bbfd864)"
+
+ // [remove-start]
+ );
+
+ const oidInterpolation = oidUuids.filtered(
+ // [remove-end]
+ // Find an item whose `_id` matches the ObjectID passed as a parameterized query argument.
+ "_id == $0",
+ oidValue
+
+ // [remove-start]
+ );
+
+ expect(oidStringLiteral.length).toBe(1);
+ expect(oidInterpolation.length).toBe(1);
+ });
+ test("UUID query", () => {
+ const oidUuids = realm.objects("OidUuid");
+ const uuid = oidUuids.filtered(
+ // [remove-end]
+ // Find an item whose `id` matches the UUID value passed to 'uuid()'.
+ "id == uuid(d1b186e1-e9e0-4768-a1a7-c492519d47ee)"
+
+ // [remove-start]
+ );
+ // prettier-ignore
+ const test = oidUuids.filtered(
+ // [remove-end]
+ // Find an item whose `_id` matches the UUID passed as a parameterized query argument.
+ "id == $0", uuidValue
+ // [snippet-end]
+ );
+ expect(uuid.length).toBe(1);
+ });
+ });
+
+ describe("Date queries", () => {
+ // Uses a test-specific schema with Date type
+ const DateTime = {
+ name: "Date",
+ properties: { name: "string", dateCompleted: "date" },
+ };
+
+ let realm;
+ const path = "date.realm";
+
+ // Add, then delete Date objects for this test
+ beforeEach(async () => {
+ realm = await Realm.open({
+ schema: [DateTime],
+ path,
+ });
+
+ realm.write(() => {
+ realm.create("Date", {
+ name: "now",
+ dateCompleted: new Date(),
+ });
+ realm.create("Date", {
+ name: "past",
+ dateCompleted: new Date("December 17, 1985 03:24:00"),
+ });
+ realm.create("Date", {
+ name: "withinYear",
+ dateCompleted: new Date("February 17, 2021 03:24:00"),
+ });
+ });
+ });
+
+ afterEach(() => {
+ realm.close();
+ Realm.deleteFile({ path });
+ });
+
+ // prettier-ignore
+ test("Date queries", () => {
+ const dates = realm.objects("Date");
+ // [snippet-start] date-alt-representation
+ var lastYear = new Date(1577883184000); // Unix timestamp in ms
+ var thisYear = new Date("2021-01-01@17:30:15:0"); // DateTime in UTC
+ var today = new Date("April 01, 2021 03:24:00"); // Alternate DateTime format
+ // [snippet-end]
+ const dateParameterizedQuery = dates.filtered(
+ // [snippet-start] date-parameterized-query
+ // Find to-do items completed before today's date.
+ "dateCompleted < $0", today
+
+ // [remove-start]
+ );
+
+ const dateAlt1 = dates.filtered(
+ // [remove-end]
+ // Find to-do items completed between the start of the year until today.
+ "dateCompleted > $0 AND dateCompleted < $1", thisYear, today
+ // [snippet-end]
+ );
+
+ expect(dateParameterizedQuery.length).toBe(2);
+ expect(dateAlt1.length).toBe(1);
+ });
+ });
+
+ // prettier-ignore
+ test("Full-text search (FTS) query", () => {
+ const items = realm.objects(Item);
+
+ const itemsWithWrite = items.filtered(
+ // [snippet-start] rql-fts
+ // Find items with 'write' in the name.
+ "name TEXT $0", "write"
+
+ // [remove-start]
+ );
+
+ const itemsWithWriteNotTest = items.filtered(
+ // [remove-end]
+ // Use '-' to exclude:
+ // Find items with 'write' but not 'tests' in the name.
+ "name TEXT $0", "write -tests"
+
+ // [remove-start]
+ );
+
+ const itemsStartingWithWri = items.filtered(
+ // [remove-end]
+ // Use '*' to match any characters after a prefix:
+ // Find items with a name that starts with 'wri'.
+ "name TEXT $0", "wri*"
+ // [snippet-end]
+ );
+ expect(itemsWithWrite.length).toBe(2);
expect(itemsWithWriteNotTest.length).toBe(0);
- expect(itemsStartingWithWri.length).toBe(1);
+ expect(itemsStartingWithWri.length).toBe(2);
});
-});
\ No newline at end of file
+});
diff --git a/examples/node/v12/__tests__/realm-query-language.test.ts b/examples/node/v12/__tests__/realm-query-language.test.ts
new file mode 100644
index 0000000000..fb16a7e8cc
--- /dev/null
+++ b/examples/node/v12/__tests__/realm-query-language.test.ts
@@ -0,0 +1,988 @@
+import Realm, { BSON } from "realm";
+import { Address, Item, Office, Project } from "./models/rql-data-models.ts";
+import { describe, expect } from "@jest/globals";
+
+describe("Realm Query Language Reference", () => {
+ let realm: Realm;
+ const config = { schema: [Project, Item, Address, Office] };
+
+ beforeEach(async () => {
+ // Before each test, open the realm and create project & item objects:
+ // 2 branches w/ 2 addresses; 3 projects; and
+ // 6 items (one used in 2 projects, another in 0 projects)
+ realm = await Realm.open(config);
+
+ const mainBranch = {
+ name: "Main Branch",
+ address: {
+ name: "Main Branch",
+ street: "999 Big Boulevard",
+ zipcode: 10019,
+ },
+ };
+ const austinBranch = {
+ name: "Austin Branch",
+ address: {
+ name: "Austin Branch",
+ street: "123 Main Ave",
+ zipcode: 10019,
+ },
+ };
+ realm.write(() => {
+ realm.create("Item", {
+ // Unassigned incomplete item not in any project
+ _id: new BSON.ObjectId("631a073c833a34ade21db2b2"),
+ name: "Write tests",
+ isComplete: false,
+ assignee: null,
+ priority: 10,
+ progressMinutes: 0,
+ });
+
+ realm.create("Project", {
+ _id: new BSON.ObjectId(),
+ name: "Example Project with Items",
+ items: [
+ {
+ // Alex incomplete item
+ _id: new BSON.ObjectId("631a072f75120729dc9223d9"),
+ name: "Write tests",
+ isComplete: false,
+ assignee: "Alex",
+ priority: 5,
+ progressMinutes: 125,
+ },
+ {
+ // Ali incomplete item
+ _id: new BSON.ObjectId("631a0737c98f89f5b81cd24d"),
+ name: "Run tests",
+ isComplete: false,
+ assignee: "Ali",
+ priority: 9,
+ progressMinutes: 10,
+ },
+ ],
+ quota: 1, // doesn't meet quota
+ comments: { status: "Behind schedule", projectNumber: "70150" },
+ projectLocation: mainBranch,
+ // Mixed property is of type dictionary of mixed values
+ // (date, list of strings, bool, and int)
+ additionalInfo: {
+ startDate: new Date("2021-01-01"),
+ customerContacts: ["Alice", "Bob"],
+ recurringCustomer: true,
+ budget: 10000,
+ },
+ });
+ const project = realm.create("Project", {
+ _id: new BSON.ObjectId(),
+ name: "Project that Meets Quota",
+ items: [
+ {
+ // Complete item used in 2 projects
+ _id: new BSON.ObjectId(),
+ name: "Approve project plan",
+ isComplete: true,
+ assignee: "Dachary",
+ priority: 1,
+ progressMinutes: 0,
+ },
+ {
+ // Complete high-priority item
+ _id: new BSON.ObjectId(),
+ name: "Create a ticket",
+ isComplete: true,
+ assignee: "Chris",
+ priority: 9,
+ progressMinutes: 10,
+ },
+ {
+ // Incomplete high-priority item
+ _id: new BSON.ObjectId(),
+ name: "Demo template app",
+ isComplete: false,
+ assignee: "Dachary",
+ priority: 11,
+ progressMinutes: 3,
+ },
+ ],
+ quota: 1, // meets quota
+ comments: { status: "Ahead of schedule", projectNumber: "70187" },
+ projectLocation: mainBranch,
+ // Mixed property is of type string
+ additionalInfo: "Customer is a VIP.",
+ });
+
+ realm.create("Project", {
+ _id: new BSON.ObjectId(),
+ name: "Project in Austin",
+ items: [
+ project.items[0],
+ {
+ // Incomplete high-priority item assigned to `nil` type
+ _id: new BSON.ObjectId(),
+ name: "Lead offsite workshop",
+ isComplete: false,
+ assignee: "nil",
+ priority: 10,
+ progressMinutes: 0,
+ },
+ ],
+ quota: 11, // doesn't meet quota
+ comments: { status: "On track", projectNumber: "N/A" },
+ projectLocation: austinBranch,
+ // Mixed property is of type list, containing string and nested
+ // dictionary of mixed values (date, boolean, and int)
+ additionalInfo: [
+ "Customer is difficult to work with.",
+ {
+ startDate: new Date("2021-03-01"),
+ recurringCustomer: false,
+ budget: 10000,
+ },
+ ],
+ });
+ });
+ });
+
+ afterEach(() => {
+ // After the test, delete the objects and close the realm
+ if (realm && !realm.isClosed) {
+ realm.write(() => {
+ realm.deleteAll();
+ });
+ realm.close();
+ expect(realm.isClosed).toBe(true);
+ }
+ });
+
+ afterAll(() => {
+ Realm.deleteFile(config);
+ });
+
+ test("Can open realm and create objects", async () => {
+ expect(realm.isClosed).toBe(false);
+ expect(realm.objects(Project)[0].name).toBe("Example Project with Items");
+ expect(realm.objects(Item)[0].name).toBe("Write tests");
+ });
+
+ test("Simple query", () => {
+ // NOTE: snippet used on Node.js Query Data page, not RQL page
+ // :snippet-start: simple-query
+ const items = realm.objects(Item);
+ // Get all items where 'priority' property is 7 or more
+ const importantItems = items.filtered("priority >= $0", 7);
+ // :snippet-end:
+ expect(importantItems.length).toEqual(5);
+ });
+
+ describe("Basic syntax", () => {
+ test("Expression query", () => {
+ const items = realm.objects(Item);
+ // prettier-ignore
+ const expression =
+ // :snippet-start: predicate
+ "priority == 1"
+ ; // :remove:
+ // Property Name: priority
+ // Operator: ==
+ // Value: 1
+ // :snippet-end:
+ expect(items.filtered(expression).length).toBe(1);
+ });
+ test("Serialized query", () => {
+ const items = realm.objects(Item);
+ const query = items.filtered(
+ // :snippet-start: serialized-query
+ "progressMinutes > 1 AND assignee == 'Ali'"
+ // :snippet-end:
+ );
+ expect(query.length).toBe(1);
+ });
+ test("Parameterized query", () => {
+ const items = realm.objects(Item);
+ // prettier-ignore
+ const substitution = items.filtered(
+ // :snippet-start: parameterized-query
+ // Include one parameter with `$0`
+ "progressMinutes > 1 AND assignee == $0", "Ali"
+
+ // :remove-start:
+ );
+ expect(substitution.length).toBe(1);
+ });
+
+ test("Multiple parameterized query", () => {
+ const items = realm.objects(Item);
+ // prettier-ignore
+ const substitution = items.filtered(
+ // :remove-end:
+ // Include multiple parameters using ascending integers,
+ // starting at`$0`
+ "progressMinutes > $0 AND assignee == $1", 1, "Alex"
+ // :snippet-end:
+ );
+ expect(substitution.length).toBe(1);
+ });
+
+ test("Dot notation", () => {
+ const address = realm.objects(Project);
+ const nestedItem = address.filtered(
+ // :snippet-start: dot-notation
+ // Find projects whose `items` list property contains an item
+ // with a specific name
+ "items[0].name == 'Approve project plan'"
+ // :snippet-end:
+ );
+ const nestedZipcode = address.filtered(
+ // :snippet-start: deep-dot-notation
+ // Find projects whose `projectLocation` property contains an
+ // embedded Address object with a specific zip code
+ "projectLocation.address.zipcode == 10019"
+ // :snippet-end:
+ );
+ expect(nestedItem.length).toBe(2);
+ expect(nestedZipcode.length).toBe(3);
+ });
+ });
+
+ // prettier-ignore
+ test("Comparison operators", () => {
+ const items = realm.objects(Item);
+
+ const highPriorityItems = items.filtered(
+ // :snippet-start: comparison-operators
+ // Compare `priority` values against a threshold value
+ "priority > $0", 5
+
+ // :remove-start:
+ );
+ expect(highPriorityItems.length).toBe(5);
+
+ const unassignedItems = items.filtered(
+ // :remove-end:
+ // Compare `assignee` values to `null` value.
+ "assignee == $0", null
+
+ // :remove-start:
+ );
+ expect(unassignedItems.length).toBe(1);
+
+ const progressMinutesRange = items.filtered(
+ // :remove-end:
+ // Compare `priority` values against an inclusive range of values
+ "priority BETWEEN { $0 , $1 }", 1, 5
+
+ // :remove-start:
+ );
+ expect(progressMinutesRange.length).toBe(2);
+
+ const progressMinutesIn = items.filtered(
+ // :remove-end:
+ // Compare `progressMinutes` values against any of the listed values
+ "progressMinutes IN { $0, $1, $2 }", 10, 30, 60
+ // :snippet-end:
+ );
+ });
+
+ // prettier-ignore
+ test("Logical operators", () => {
+ const items = realm.objects(Item);
+ const aliComplete = items.filtered(
+ // :snippet-start: logical-operators
+ // Find all items assigned to Ali AND marked completed
+ "assignee == $0 AND isComplete == $1", "Ali", true
+
+ // :remove-start:
+ );
+ const alexOrAli = items.filtered(
+ // :remove-end:
+ // Find all items assigned to Alex OR to Ali
+ "assignee == $0 OR assignee == $1", "Alex", "Ali"
+ // :snippet-end:
+ );
+ expect(aliComplete.length).toBe(0);
+ expect(alexOrAli.length).toBe(2);
+ });
+
+ describe("Arithmetic operators", () => {
+ test("Basic arithmetic", () => {
+ const items = realm.objects(Item);
+ const basicMath = items.filtered(
+ // :snippet-start: basic-arithmetic
+ // Evaluate against an item's `priority` property value:
+ "2 * priority > 6" // (resolves to `priority > 3`)
+
+ // :remove-start:
+ );
+ const lessBasicMath = items.filtered(
+ // :remove-end:
+ "priority >= 2 * (2 - 1) + 2" // (resolves to `priority >= 4`)
+
+ // :remove-start:
+ );
+ expect(basicMath.length).toBe(6);
+ expect(lessBasicMath.length).toBe(6);
+ });
+
+ test("Arithmetic with object properties", () => {
+ const items = realm.objects(Item);
+ const mathWithObjProps = items.filtered(
+ // :remove-end:
+ // Evaluate against multiple object property values:
+ "progressMinutes * priority == 90"
+ // :snippet-end:
+ );
+ expect(mathWithObjProps.length).toBe(2);
+ });
+ });
+
+ // prettier-ignore
+ describe("Type-specific operators", () => {
+ test("String operators", () => {
+ const projects = realm.objects(Project);
+ const startWithE = projects.filtered(
+ // :snippet-start: string-operators
+ // Find projects whose name starts with 'E' or 'e'
+ // (case-insensitive query)
+ "name BEGINSWITH[c] $0", "E"
+
+ // :remove-start:
+ );
+ expect(startWithE.length).toBe(1);
+
+ const containIe = projects.filtered(
+ // :remove-end:
+ // Find projects whose name contains 'ie'
+ // (case-sensitive query)
+ "name CONTAINS $0", "ie"
+
+ // :remove-start:
+ );
+ expect(containIe.length).toBe(0);
+ });
+
+ test("String comparisons", () => {
+ const projects = realm.objects(Project);
+ const items = realm.objects(Item);
+ // prettier-ignore
+ const assigneeBetween = items.filtered(
+ // :remove-end:
+ // Find items where the assignee name is lexicographically between
+ // 'Ali' and 'Chris'(case-sensitive)
+ "assignee BETWEEN { $0 , $1 }", "Ali", "Chris"
+
+ // :remove-start:
+ );
+ // prettier-ignore
+ const compareStreet = projects.filtered(
+ // :remove-end:
+ // Find projects where the street address is lexicographically
+ // greater than '123 Main St'(case-sensitive)
+ "projectLocation.address.street > $0", "123 Main St"
+ // :snippet-end:
+ );
+ expect(compareStreet.length).toBe(2);
+ expect(assigneeBetween.length).toBe(2);
+ });
+ });
+
+ // prettier-ignore
+ test("Aggregate queries", () => {
+ const projects = realm.objects(Project);
+ // :snippet-start: aggregate-operators
+ var priorityNum = 5;
+
+ // :remove-start:
+ const averageItemPriorityAbove5 = projects.filtered(
+ // :remove-end:
+ // Find projects with average item `priority` above 5
+ "items.@avg.priority > $0", priorityNum
+
+ // :remove-start:
+ );
+ expect(averageItemPriorityAbove5.length).toBe(3);
+
+ const allItemsLowerPriority = projects.filtered(
+ // :remove-end:
+ // Find projects where every item has a `priority` less than 5
+ "items.@max.priority < $0", priorityNum
+
+ // :remove-start:
+ );
+ expect(allItemsLowerPriority.length).toBe(0);
+
+ const allItemsHighPriority = projects.filtered(
+ // :remove-end:
+ // Find projects where every item has `priority` greater than 5
+ "items.@min.priority > $0", priorityNum
+
+ // :remove-start:
+ );
+ expect(allItemsHighPriority.length).toBe(0);
+
+ const moreThan5Items = projects.filtered(
+ // :remove-end:
+ // Find projects with more than 5 items
+ "items.@count > $0", 5
+
+ // :remove-start:
+ );
+ expect(moreThan5Items.length).toBe(0);
+
+ const longRunningProjects = projects.filtered(
+ // :remove-end:
+ // Find projects where the sum total value of `progressMinutes`
+ // across all items is greater than 100
+ "items.@sum.progressMinutes > $0",
+ 100
+ // :snippet-end:
+ );
+ expect(longRunningProjects.length).toBe(1);
+ });
+
+ describe("Collection operators", () => {
+ test("Collection queries", () => {
+ const projects = realm.objects(Project);
+ // prettier-ignore
+ const noCompleteItems = projects.filtered(
+ // :snippet-start: set-operators
+ // Find projects with no complete items
+ "NONE items.isComplete == $0", true
+
+ // :remove-start:
+ );
+ // prettier-ignore
+ const anyTopPriorityItems = projects.filtered(
+ // :remove-end:
+ // Find projects that contain any item with priority 10
+ "items.priority == $0", 10 // (ANY operator is implied)
+
+ // :remove-start:
+ );
+ // prettier-ignore
+ const allItemsCompleted = projects.filtered(
+ // :remove-end:
+ // Find projects that only contain completed items
+ "ALL items.isComplete == $0", true
+
+ // :remove-start:
+ );
+ // prettier-ignore
+ const assignedToAlexOrAli = projects.filtered(
+ // :remove-end:
+ // Find projects with at least one item assigned to
+ // either Alex or Ali
+ "ANY items.assignee IN { $0 , $1 }", "Alex", "Ali"
+
+ // :remove-start:
+ );
+ // prettier-ignore
+ const notAssignedToAlexOrAli = projects.filtered(
+ // :remove-end:
+ // Find projects with no items assigned to either Alex or Ali
+ "NONE items.assignee IN { $0 , $1 }", "Alex", "Ali"
+ // :snippet-end:
+ );
+ expect(noCompleteItems.length).toBe(1);
+ expect(anyTopPriorityItems.length).toBe(1);
+ expect(allItemsCompleted.length).toBe(0);
+ expect(assignedToAlexOrAli.length).toBe(1);
+ expect(notAssignedToAlexOrAli.length).toBe(2);
+ });
+
+ test("List comparisons", () => {
+ realm.write(() => {});
+ const projects = realm.objects(Project);
+ const items = realm.objects(Item);
+
+ const collectionQuery = projects.filtered(
+ // :snippet-start: list-comparisons-collection
+ // Find an item with the specified ObjectId value
+ // in the`items` collection
+ "oid(631a072f75120729dc9223d9) IN items._id"
+ // :snippet-end:
+ );
+ const staticQuery = items.filtered(
+ // :snippet-start: list-comparisons-static
+ // Find items with a priority value matching any value
+ // in the static list
+ "priority IN {0, 1, 2}"
+ // :snippet-end:
+ );
+ // :snippet-start: list-comparisons-parameterized
+ const ids = [
+ new BSON.ObjectId("631a072f75120729dc9223d9"),
+ new BSON.ObjectId("631a0737c98f89f5b81cd24d"),
+ new BSON.ObjectId("631a073c833a34ade21db2b2"),
+ ];
+ // Find items with an ObjectId value matching any value
+ // in the parameterized list
+ const parameterizedQuery = realm.objects(Item).filtered("_id IN $0", ids);
+ // :snippet-end:
+
+ // prettier-ignore
+ const anyOperator = items.filtered(
+ // :snippet-start: equivalent-lists-any-operator
+ "assignee == ANY { $0, $1 }", "Alex", "Ali"
+
+ // :remove-start:
+ );
+ // prettier-ignore
+ const equivalentAnyOperator = items.filtered(
+ // :remove-end:
+ "assignee == { $0, $1 }", "Alex", "Ali" // Equivalent (ANY is implied)
+ // :snippet-end:
+ );
+
+ expect(anyOperator.length).toBe(2);
+ expect(equivalentAnyOperator.length).toBe(2);
+ expect(collectionQuery.length).toBe(1);
+ expect(staticQuery.length).toBe(1);
+ expect(parameterizedQuery.length).toBe(3);
+ });
+
+ test("Sort, distinct, and limit results", () => {
+ const items = realm.objects(Item);
+
+ const sortedItems = items.filtered(
+ // :snippet-start: sort-distinct-limit
+ // Find incomplete items, sort by `priority` in descending order,
+ // then sort equal `priority` values by `progressMinutes`
+ // in ascending order
+ "isComplete == false SORT(priority DESC, progressMinutes ASC)"
+
+ // :remove-start:
+ );
+ expect(sortedItems[0].name).toBe("Demo template app");
+ const distinctItems = items.filtered(
+ // :remove-end:
+ // Find high priority items, then remove from the results any items
+ // with duplicate values for both `name` AND `assignee` properties
+ "priority >= 5 DISTINCT(name, assignee)"
+
+ // :remove-start:
+ );
+ expect(distinctItems.length).toBe(6);
+ const limitItems = items.filtered(
+ // :remove-end:
+ // Find in-progress items, then return the first 10 results
+ "progressMinutes > 0 && isComplete != true LIMIT(10)"
+ // :snippet-end:
+ );
+ expect(limitItems[0].name).toBe("Write tests");
+
+ const sortFirst = items.filtered(
+ // :snippet-start: sort-distinct-limit-order-matters
+ // 1. Sorts by highest priority
+ // 2. Returns the first item
+ // 3. Remove duplicate names (N/A - a single item is always distinct)
+ "assignee == null SORT(priority ASC) LIMIT(1) DISTINCT(name)"
+
+ // :remove-start:
+ );
+ const limitLast = items.filtered(
+ // :remove-end:
+ // 1. Removes any duplicates by name
+ // 2. Sorts by highest priority
+ // 3. Returns the first item
+ "assignee == null DISTINCT(name) SORT(priority ASC) LIMIT(1)"
+ // :snippet-end:
+ );
+ });
+
+ test("Subquery query", () => {
+ const projects = realm.objects("Project");
+ const subquery = projects.filtered(
+ // :snippet-start: subquery
+ // Find projects with incomplete items with 'Demo' in the name
+ "SUBQUERY(items, $item, $item.isComplete == false AND $item.name CONTAINS[c] 'Demo').@count > 0"
+
+ // :remove-start:
+ );
+ expect(subquery.length).toBe(1);
+ expect(subquery[0].name).toBe("Project that Meets Quota");
+
+ const subquery2 = projects.filtered(
+ // :remove-end:
+ // Find projects where the number of completed items is
+ // greater than or equal to the project's `quota` property
+ "SUBQUERY(items, $item, $item.isComplete == true).@count >= quota"
+ // :snippet-end:
+ );
+
+ expect(subquery2.length).toBe(1);
+ expect(subquery2[0].name).toBe("Project that Meets Quota");
+ });
+
+ // prettier-ignore
+ test("Dictionary operators", () => {
+ const dictionaries = realm.objects(Project);
+ const statusKey = dictionaries.filtered(
+ // :snippet-start: dictionary-operators
+ // Find projects whose `comments` dictionary property
+ // have a key of 'status'
+ "comments.@keys == $0", "status"
+
+ // :remove-start:
+ );
+ const statusOnTrack = dictionaries.filtered(
+ // :remove-end:
+ // Find projects whose `comments` dictionary property
+ // have a 'status' key with a value that ends in 'track'
+ "comments['status'] LIKE $0", "*track"
+
+ // :remove-start:
+ );
+ const numItemsInDict = dictionaries.filtered(
+ // :remove-end:
+ // Find projects whose `comments` dictionary property
+ // have more than one key-value pair
+ "comments.@count > $0", 1
+
+ // :remove-start:
+ );
+
+ const allString = dictionaries.filtered(
+ // :remove-end:
+ // Find projects whose `comments` dictionary property contains
+ // only values of type 'string'
+ "ALL comments.@type == 'string'"
+
+ // :remove-start:
+ );
+
+ const noInts = dictionaries.filtered(
+ // :remove-end:
+ // Find projects whose `comments` dictionary property contains
+ // no values of type 'int'
+ "NONE comments.@type == 'int'"
+ // :snippet-end:
+ );
+ expect(statusKey.length).toBe(3);
+ expect(statusOnTrack.length).toBe(1);
+ expect(numItemsInDict.length).toBe(3);
+ expect(allString.length).toBe(3);
+ expect(noInts.length).toBe(3);
+ });
+ });
+
+ describe("Backlinks queries", () => {
+ test("Backlinks query @links", () => {
+ const atLinksResult = realm.objects(Item).filtered(
+ // :snippet-start: backlinks-atLinks
+ // Find items that belong to a project with a quota less than 10
+ // (using '@links..')
+ "@links.Project.items.quota < 10"
+ // :snippet-end:
+ );
+ expect(atLinksResult.length).toBe(5);
+
+ const linkingObjectsResult = realm.objects(Item).filtered(
+ // :snippet-start: backlinks-linkingObjects
+ // Find items that belong to a project with a quota greater than 10
+ // through the Item object's `projects` property
+ // (using 'LinkingObjects')
+ "projects.quota > 10"
+ // :snippet-end:
+ );
+ expect(linkingObjectsResult.length).toBe(2);
+ });
+
+ test("Backlinks collection operators", () => {
+ const anyResult = realm.objects(Item).filtered(
+ // :snippet-start: backlinks-collection-operators
+ // Find items where no project that references the item has a
+ // quota greater than 10
+ "NONE @links.Project.items.quota > 10"
+
+ // :remove-start:
+ );
+ expect(anyResult.length).toBe(5);
+
+ const allResult = realm.objects(Item).filtered(
+ // :remove-end:
+ // Find items where all projects that reference the item have a
+ // quota less than 5
+ "ALL @links.Project.items.quota < 5"
+
+ // :remove-start:
+ );
+ expect(allResult.length).toBe(5);
+ });
+
+ test("Backlinks aggregate operators", () => {
+ const shallowResultLinkingObjects = realm.objects(Item).filtered(
+ // :remove-end:
+ // Find items that are referenced by multiple projects
+ "projects.@count > 1"
+
+ // :remove-start:
+ );
+ expect(shallowResultLinkingObjects.length).toBe(1);
+ expect(shallowResultLinkingObjects[0].name).toBe("Approve project plan");
+
+ const shallowResultAtLinks = realm.objects(Item).filtered(
+ // :remove-end:
+ // Find items that are not referenced by any project
+ "@links.Project.items.@count == 0"
+
+ // :remove-start:
+ );
+ expect(shallowResultAtLinks.length).toBe(1);
+ expect(shallowResultAtLinks[0].name).toBe("Write tests");
+
+ const deepResultAtLinks = realm.objects(Item).filtered(
+ // :remove-end:
+ // Find items that belong to a project where the average item
+ // has been worked on for at least 10 minutes
+ "@links.Project.items.items.@avg.progressMinutes > 10"
+
+ // :remove-start:
+ );
+ expect(deepResultAtLinks.length).toBe(2);
+ expect(deepResultAtLinks[0].name).toBe("Write tests");
+ });
+
+ test("Count all backlinks (@links.@count)", () => {
+ const result = realm.objects(Item).filtered(
+ // :remove-end:
+ // Find items that are not referenced by another object
+ // of any type (backlink count is 0)
+ "@links.@count == 0"
+ // :snippet-end:
+ );
+ expect(result.length).toBe(1);
+ expect(result[0].name).toBe("Write tests");
+ });
+ });
+
+ describe("Type operators", () => {
+ test("Type operator", () => {
+ const projects = realm.objects(Project);
+ const mixedString = projects.filtered(
+ // :snippet-start: type-operator
+ // Find projects with an `additionalInfo` property of string type
+ "additionalInfo.@type == 'string'"
+
+ // :remove-start:
+ );
+ const mixedCollection = projects.filtered(
+ // :remove-end:
+ // Find projects with an `additionalInfo` property of
+ // `collection` type, which matches list or dictionary types
+ "additionalInfo.@type == 'collection'"
+
+ // :remove-start:
+ );
+ const mixedBool = projects.filtered(
+ // :remove-end:
+ // Find projects with an `additionalInfo` property of list type,
+ // where any list element is of type 'bool'
+ "additionalInfo[*].@type == 'bool'"
+ // :snippet-end:
+ );
+ expect(mixedString.length).toBe(1);
+ expect(mixedBool.length).toBe(1);
+ });
+ });
+
+ test("Nil type", () => {
+ const items = realm.objects(Item);
+ const res = items.filtered(
+ // :snippet-start: nil-type
+ "assignee == nil"
+ // :remove-start:
+ );
+ // prettier-ignore
+ const res2 = realm.objects(Item).filtered(
+ // :remove-end:
+ "assignee == $0", null // 'null' maps to SDK language's null pointer
+ // :snippet-end:
+ );
+ expect(res.length).toBe(1);
+ expect(res2.length).toBe(1);
+ });
+
+ describe("ObjectId and UUID queries", () => {
+ // Uses a test-specific schema with id types
+ const OidUuid = {
+ name: "OidUuid",
+ properties: { id: "uuid", _id: "objectId" },
+ };
+
+ let realm: Realm;
+ const path = "oidUuid.realm";
+
+ const oidValueString = "6001c033600510df3bbfd864";
+ const uuid1String = "d1b186e1-e9e0-4768-a1a7-c492519d47ee";
+ const oidValue = new BSON.ObjectId(oidValueString);
+ const uuidValue = new BSON.UUID(uuid1String);
+
+ // Add, then delete objects for this test
+ beforeEach(async () => {
+ realm = await Realm.open({ schema: [OidUuid], path });
+ const obj1 = {
+ _id: oidValue,
+ id: uuidValue,
+ };
+ const obj2 = {
+ _id: new BSON.ObjectId(),
+ id: new BSON.UUID(),
+ };
+ realm.write(() => {
+ realm.create("OidUuid", obj1);
+ realm.create("OidUuid", obj2);
+ });
+ });
+
+ afterEach(() => {
+ realm.close();
+ Realm.deleteFile({ path });
+ });
+
+ test("ObjectId query", () => {
+ const oidUuids = realm.objects("OidUuid");
+ // prettier-ignore
+ const oidStringLiteral = oidUuids.filtered(
+ // :snippet-start: oid
+ // Find an item whose `_id` matches the ObjectID value
+ // passed to 'oid()'
+ "_id == oid(6001c033600510df3bbfd864)"
+
+ // :remove-start:
+ );
+
+ const oidInterpolation = oidUuids.filtered(
+ // :remove-end:
+ // Find an item whose `_id` matches the ObjectID passed as
+ // a parameterized query argument
+ "_id == $0", oidValue
+
+ // :remove-start:
+ );
+
+ expect(oidStringLiteral.length).toBe(1);
+ expect(oidInterpolation.length).toBe(1);
+ });
+ test("UUID query", () => {
+ const oidUuids = realm.objects("OidUuid");
+ const uuid = oidUuids.filtered(
+ // :remove-end:
+ // Find an item whose `id` matches the UUID value
+ // passed to 'uuid()'
+ "id == uuid(d1b186e1-e9e0-4768-a1a7-c492519d47ee)"
+
+ // :remove-start:
+ );
+ // prettier-ignore
+ const test = oidUuids.filtered(
+ // :remove-end:
+ // Find an item whose `_id` matches the UUID passed as
+ // a parameterized query argument
+ "id == $0", uuidValue
+ // :snippet-end:
+ );
+ expect(uuid.length).toBe(1);
+ });
+ });
+
+ describe("Date queries", () => {
+ // Uses a test-specific schema with Date type
+ const DateTime = {
+ name: "Date",
+ properties: { name: "string", dateCompleted: "date" },
+ };
+
+ let realm: Realm;
+ const path = "date.realm";
+
+ // Add, then delete Date objects for this test
+ beforeEach(async () => {
+ realm = await Realm.open({
+ schema: [DateTime],
+ path,
+ });
+
+ realm.write(() => {
+ realm.create("Date", {
+ name: "now",
+ dateCompleted: new Date(),
+ });
+ realm.create("Date", {
+ name: "past",
+ dateCompleted: new Date("December 17, 1985 03:24:00"),
+ });
+ realm.create("Date", {
+ name: "withinYear",
+ dateCompleted: new Date("February 17, 2021 03:24:00"),
+ });
+ });
+ });
+
+ afterEach(() => {
+ realm.close();
+ Realm.deleteFile({ path });
+ });
+
+ // prettier-ignore
+ test("Date queries", () => {
+ const dates = realm.objects("Date");
+ // :snippet-start: date-alt-representation
+ var lastYear = new Date(1577883184000); // Unix timestamp in ms
+ var thisYear = new Date("2021-01-01@17:30:15:0"); // DateTime in UTC
+ var today = new Date("April 01, 2021 03:24:00"); // Alternate DateTime format
+ // :snippet-end:
+ const dateParameterizedQuery = dates.filtered(
+ // :snippet-start: date-parameterized-query
+ // Find to-do items completed before today's date
+ "dateCompleted < $0", today
+
+ // :remove-start:
+ );
+
+ const dateAlt1 = dates.filtered(
+ // :remove-end:
+ // Find to-do items completed between start of the year to today
+ "dateCompleted > $0 AND dateCompleted < $1", thisYear, today
+ // :snippet-end:
+ );
+
+ expect(dateParameterizedQuery.length).toBe(2);
+ expect(dateAlt1.length).toBe(1);
+ });
+ });
+
+ // prettier-ignore
+ test("Full-text search (FTS) query", () => {
+ const items = realm.objects(Item);
+
+ const itemsWithWrite = items.filtered(
+ // :snippet-start: rql-fts
+ // Find items with 'write' in the name.
+ "name TEXT $0", "write"
+
+ // :remove-start:
+ );
+
+ const itemsWithWriteNotTest = items.filtered(
+ // :remove-end:
+ // Use '-' to exclude:
+ // Find items with 'write' but not 'tests' in the name
+ "name TEXT $0", "write -tests"
+
+ // :remove-start:
+ );
+
+ const itemsStartingWithWri = items.filtered(
+ // :remove-end:
+ // Use '*' to match any characters after a prefix:
+ // Find items with a name that starts with 'wri'
+ "name TEXT $0", "wri*"
+ // :snippet-end:
+ );
+ expect(itemsWithWrite.length).toBe(2);
+ expect(itemsWithWriteNotTest.length).toBe(0);
+ expect(itemsStartingWithWri.length).toBe(2);
+ });
+});
diff --git a/examples/node/v12/__tests__/rql-data-models.test.js b/examples/node/v12/__tests__/rql-data-models.test.js
new file mode 100644
index 0000000000..2a6500fb53
--- /dev/null
+++ b/examples/node/v12/__tests__/rql-data-models.test.js
@@ -0,0 +1,71 @@
+import Realm, { BSON } from "realm";
+import { Item, Project, Office, Address } from "./models/rql-data-models.ts";
+
+describe("Test RQL Models", () => {
+ let realm;
+ const config = { schema: [Project, Item, Office, Address] };
+
+ beforeEach(async () => {
+ realm = await Realm.open(config);
+ });
+
+ afterEach(() => {
+ // After each test, delete the objects and close the realm
+ if (realm && !realm.isClosed) {
+ realm.write(() => {
+ realm.deleteAll();
+ });
+ realm.close();
+ expect(realm.isClosed).toBe(true);
+ }
+ });
+
+ afterAll(() => {
+ Realm.deleteFile(config);
+ });
+
+ test("Can open realm with config", async () => {
+ expect(realm.isClosed).toBe(false);
+ });
+
+ test("Can create object of Item type", () => {
+ realm.write(() => {
+ realm.create(Item, {
+ _id: new BSON.ObjectId(),
+ name: "get coffee",
+ });
+ });
+ const coffeeItem = realm.objects(Item)[0];
+ expect(coffeeItem._id instanceof BSON.ObjectId).toBe(true);
+ expect(coffeeItem.name).toBe("get coffee");
+ expect(coffeeItem.isComplete).toBe(false);
+ });
+
+ test("Can create object of Project type", () => {
+ realm.write(() => {
+ const teaItem = realm.create(Item, {
+ _id: new BSON.ObjectId(),
+ name: "get tea",
+ });
+ const officeAddress = realm.create(Office, {
+ name: "Austin",
+ address: {
+ name: "Main Branch",
+ street: "123 Main St",
+ zipcode: 10019,
+ },
+ });
+ realm.create(Project, {
+ _id: new BSON.ObjectId(),
+ name: "beverages",
+ items: [teaItem],
+ projectLocation: officeAddress,
+ });
+ });
+ const bevProject = realm.objects(Project)[0];
+ expect(bevProject._id instanceof BSON.ObjectId).toBe(true);
+ expect(bevProject.name).toBe("beverages");
+ expect(bevProject.items[0].name).toBe("get tea");
+ expect(bevProject.projectLocation.name).toBe("Austin");
+ });
+});
diff --git a/examples/node/v12/__tests__/rql-data-models.test.ts b/examples/node/v12/__tests__/rql-data-models.test.ts
new file mode 100644
index 0000000000..046b94436b
--- /dev/null
+++ b/examples/node/v12/__tests__/rql-data-models.test.ts
@@ -0,0 +1,81 @@
+import Realm, { BSON } from "realm";
+import { Item, Project, Office, Address } from "./models/rql-data-models.ts";
+
+describe("Test RQL Models", () => {
+ let realm: Realm;
+ const config = { schema: [Project, Item, Office, Address] };
+
+ beforeEach(async () => {
+ realm = await Realm.open(config);
+ });
+
+ afterEach(() => {
+ if (realm && !realm.isClosed) {
+ realm.write(() => {
+ realm.deleteAll();
+ });
+ realm.close();
+ expect(realm.isClosed).toBe(true);
+ }
+ });
+
+ afterAll(() => {
+ Realm.deleteFile(config);
+ });
+
+ test("Can open realm with config", async () => {
+ expect(realm.isClosed).toBe(false);
+ });
+
+ test("Can create object of Item type", () => {
+ const itemId = new BSON.ObjectId();
+ realm.write(() => {
+ realm.create(Item, {
+ _id: itemId,
+ name: "get coffee",
+ });
+ });
+ const coffeeItem = realm.objects(Item)[0];
+ expect(coffeeItem._id).toEqual(itemId);
+ expect(coffeeItem.name).toBe("get coffee");
+ expect(coffeeItem.isComplete).toBe(false);
+ });
+
+ test("Can create object of Project type", () => {
+ const projectId = new BSON.ObjectId();
+ realm.write(() => {
+ // Create the tea item
+ const teaItem = realm.create("Item", {
+ _id: new BSON.ObjectId(),
+ name: "get tea",
+ });
+
+ // Create the address object
+ const address = {
+ name: "Main Branch",
+ street: "123 Main St",
+ zipcode: 10019,
+ };
+
+ // Create the office object
+ const office = realm.create("Office", {
+ name: "Main Office",
+ address: address,
+ });
+ // Create the project object
+ realm.create("Project", {
+ _id: projectId,
+ name: "beverages",
+ items: [teaItem],
+ projectLocation: office,
+ });
+ });
+
+ const bevProject = realm.objects(Project)[0];
+ expect(bevProject._id).toEqual(projectId);
+ expect(bevProject.name).toBe("beverages");
+ expect(bevProject.items[0].name).toBe("get tea");
+ expect(bevProject.projectLocation?.name).toBe("Main Office");
+ expect(bevProject.projectLocation?.address.name).toBe("Main Branch");
+ });
+});
diff --git a/examples/node/v12/package.json b/examples/node/v12/package.json
index dbfca9c35d..731ae4e7e0 100644
--- a/examples/node/v12/package.json
+++ b/examples/node/v12/package.json
@@ -5,7 +5,7 @@
"main": "index.js",
"type": "module",
"scripts": {
- "test": "jest --runInBand --detectOpenHandles --forceExit",
+ "test": "jest --runInBand --detectOpenHandles --silent=false --forceExit",
"posttest": "npm run delete-realm-files",
"test:js": "jest --selectProjects JavaScript --runInBand --detectOpenHandles --forceExit; npm run delete-realm-files",
"test:ts": "NODE_OPTIONS=--experimental-vm-modules jest --selectProjects TypeScript --runInBand --detectOpenHandles --forceExit; npm run delete-realm-files",
diff --git a/examples/package-lock.json b/examples/package-lock.json
new file mode 100644
index 0000000000..94f1164790
--- /dev/null
+++ b/examples/package-lock.json
@@ -0,0 +1,6 @@
+{
+ "name": "examples",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {}
+}
diff --git a/examples/react-native/legacy/__tests__/js/CRUD/update.test.jsx b/examples/react-native/legacy/__tests__/js/CRUD/update.test.jsx
index 4209b0317c..605b716363 100644
--- a/examples/react-native/legacy/__tests__/js/CRUD/update.test.jsx
+++ b/examples/react-native/legacy/__tests__/js/CRUD/update.test.jsx
@@ -175,7 +175,7 @@ describe('Update Data Tests', () => {
);
const carWashTask = assertionRealm.objectForPrimaryKey(Task, 1234);
- // Test that the the 'Wash the car' task was upserted, and progressMinutesText is now displaying 5 minutes progressed
+ // Test that the 'Wash the car' task was upserted, and progressMinutesText is now displaying 5 minutes progressed
expect(progressMinutesText.children.toString()).toBe('5');
expect(carWashTask.progressMinutes).toBe(5);
});
diff --git a/snooty.toml b/snooty.toml
index e6939c0f89..426594606a 100644
--- a/snooty.toml
+++ b/snooty.toml
@@ -9,6 +9,13 @@ intersphinx = [
# These are the pages that open when you click on them (instead of just being containers)
toc_landing_pages = [
+ # New IA
+ "/sdk/crud/create",
+ "/sdk/crud/read",
+ "/sdk/platforms/android",
+ "/sdk/platforms/apple",
+ "/sdk/platforms/unity",
+
# Tutorial
"/tutorial",
# SDKs
@@ -83,6 +90,8 @@ atlas = "Atlas"
admin-api-page = "/admin/api/v3/#"
base-url = "https://www.mongodb.com/docs/realm"
cpp-prefix = "https://www.mongodb.com/docs/realm-sdks/cpp/latest"
+dart-minimum-version="3.3.0"
+flutter-minimum-version="3.19.0"
kotlin-sdk-version = "1.16.0"
kotlinx-coroutines-version = "1.7.0"
kotlin-sync-prefix = "https://www.mongodb.com/docs/realm-sdks/kotlin/latest/library-sync/"
diff --git a/source/examples/MissingPlaceholders/api-java-kotlin.kt b/source/examples/MissingPlaceholders/api-java-kotlin.kt
new file mode 100644
index 0000000000..5b7abc0b78
--- /dev/null
+++ b/source/examples/MissingPlaceholders/api-java-kotlin.kt
@@ -0,0 +1 @@
+// The Java SDK does not support this API.
diff --git a/source/examples/MissingPlaceholders/api.cpp b/source/examples/MissingPlaceholders/api.cpp
new file mode 100644
index 0000000000..b5ba4125c4
--- /dev/null
+++ b/source/examples/MissingPlaceholders/api.cpp
@@ -0,0 +1 @@
+// The C++ SDK does not currently support this API.
diff --git a/source/examples/MissingPlaceholders/api.cs b/source/examples/MissingPlaceholders/api.cs
new file mode 100644
index 0000000000..63560b2a02
--- /dev/null
+++ b/source/examples/MissingPlaceholders/api.cs
@@ -0,0 +1 @@
+// The .NET SDK does not currently support this API.
diff --git a/source/examples/MissingPlaceholders/api.dart b/source/examples/MissingPlaceholders/api.dart
new file mode 100644
index 0000000000..5cc60fdb10
--- /dev/null
+++ b/source/examples/MissingPlaceholders/api.dart
@@ -0,0 +1 @@
+// The Flutter SDK does not currently support this API.
diff --git a/source/examples/MissingPlaceholders/api.java b/source/examples/MissingPlaceholders/api.java
new file mode 100644
index 0000000000..5b7abc0b78
--- /dev/null
+++ b/source/examples/MissingPlaceholders/api.java
@@ -0,0 +1 @@
+// The Java SDK does not support this API.
diff --git a/source/examples/MissingPlaceholders/api.js b/source/examples/MissingPlaceholders/api.js
new file mode 100644
index 0000000000..2cbf46abe5
--- /dev/null
+++ b/source/examples/MissingPlaceholders/api.js
@@ -0,0 +1 @@
+// The Node.js SDK does not currently support this API.
diff --git a/source/examples/MissingPlaceholders/api.kt b/source/examples/MissingPlaceholders/api.kt
new file mode 100644
index 0000000000..5804ce35f6
--- /dev/null
+++ b/source/examples/MissingPlaceholders/api.kt
@@ -0,0 +1 @@
+// The Kotlin SDK does not currently support this API.
diff --git a/source/examples/MissingPlaceholders/api.m b/source/examples/MissingPlaceholders/api.m
new file mode 100644
index 0000000000..c8a3abd96b
--- /dev/null
+++ b/source/examples/MissingPlaceholders/api.m
@@ -0,0 +1 @@
+// The Swift SDK does not currently support this API.
diff --git a/source/examples/MissingPlaceholders/api.swift b/source/examples/MissingPlaceholders/api.swift
new file mode 100644
index 0000000000..c8a3abd96b
--- /dev/null
+++ b/source/examples/MissingPlaceholders/api.swift
@@ -0,0 +1 @@
+// The Swift SDK does not currently support this API.
diff --git a/source/examples/MissingPlaceholders/api.ts b/source/examples/MissingPlaceholders/api.ts
new file mode 100644
index 0000000000..c788c96509
--- /dev/null
+++ b/source/examples/MissingPlaceholders/api.ts
@@ -0,0 +1 @@
+// This API is not currently available in TypeScript.
diff --git a/source/examples/MissingPlaceholders/example-java-kotlin.kt b/source/examples/MissingPlaceholders/example-java-kotlin.kt
new file mode 100644
index 0000000000..f17d4bf9cf
--- /dev/null
+++ b/source/examples/MissingPlaceholders/example-java-kotlin.kt
@@ -0,0 +1,2 @@
+// The documentation does not have this code example in Kotlin for the Java SDK.
+// Please refer to the other languages or related pages for example code.
\ No newline at end of file
diff --git a/source/examples/MissingPlaceholders/example.cpp b/source/examples/MissingPlaceholders/example.cpp
new file mode 100644
index 0000000000..906d6e0f8c
--- /dev/null
+++ b/source/examples/MissingPlaceholders/example.cpp
@@ -0,0 +1,2 @@
+// The documentation does not currently have this code example in C++.
+// Please refer to the other languages or related pages for example code.
diff --git a/source/examples/MissingPlaceholders/example.cs b/source/examples/MissingPlaceholders/example.cs
new file mode 100644
index 0000000000..2130048ee9
--- /dev/null
+++ b/source/examples/MissingPlaceholders/example.cs
@@ -0,0 +1,2 @@
+// The documentation does not currently have this code example in C#.
+// Please refer to the other languages or related pages for example code.
diff --git a/source/examples/MissingPlaceholders/example.dart b/source/examples/MissingPlaceholders/example.dart
new file mode 100644
index 0000000000..291e4eb0ce
--- /dev/null
+++ b/source/examples/MissingPlaceholders/example.dart
@@ -0,0 +1,2 @@
+// The documentation does not currently have this code example in Dart.
+// Please refer to the other languages or related pages for example code.
diff --git a/source/examples/MissingPlaceholders/example.java b/source/examples/MissingPlaceholders/example.java
new file mode 100644
index 0000000000..200e7e747e
--- /dev/null
+++ b/source/examples/MissingPlaceholders/example.java
@@ -0,0 +1,2 @@
+// The documentation does not have this code example in Java.
+// Please refer to the other languages or related pages for example code.
diff --git a/source/examples/MissingPlaceholders/example.js b/source/examples/MissingPlaceholders/example.js
new file mode 100644
index 0000000000..9affb156c9
--- /dev/null
+++ b/source/examples/MissingPlaceholders/example.js
@@ -0,0 +1,2 @@
+// The documentation does not currently have this code example in JavaScript.
+// Please refer to the other languages or related pages for example code.
diff --git a/source/examples/MissingPlaceholders/example.kt b/source/examples/MissingPlaceholders/example.kt
new file mode 100644
index 0000000000..e0f82450ea
--- /dev/null
+++ b/source/examples/MissingPlaceholders/example.kt
@@ -0,0 +1,2 @@
+// The documentation does not currently have this code example in Kotlin.
+// Please refer to the other languages or related pages for example code.
diff --git a/source/examples/MissingPlaceholders/example.m b/source/examples/MissingPlaceholders/example.m
new file mode 100644
index 0000000000..21da71fc02
--- /dev/null
+++ b/source/examples/MissingPlaceholders/example.m
@@ -0,0 +1,2 @@
+// The documentation does not currently have this code example in Objective-C.
+// Please refer to the other languages or related pages for example code.
diff --git a/source/examples/MissingPlaceholders/example.swift b/source/examples/MissingPlaceholders/example.swift
new file mode 100644
index 0000000000..a2510417b9
--- /dev/null
+++ b/source/examples/MissingPlaceholders/example.swift
@@ -0,0 +1,2 @@
+// The documentation does not currently have this code example in Swift.
+// Please refer to the other languages or related pages for example code.
diff --git a/source/examples/MissingPlaceholders/example.ts b/source/examples/MissingPlaceholders/example.ts
new file mode 100644
index 0000000000..855d7406ef
--- /dev/null
+++ b/source/examples/MissingPlaceholders/example.ts
@@ -0,0 +1,2 @@
+// The documentation does not currently have this code example in TypeScript.
+// Please refer to the other languages or related pages for example code.
diff --git a/source/examples/generated/dotnet/Asymmetrics.snippet.asymmetry.cs b/source/examples/generated/dotnet/Asymmetrics.snippet.asymmetry.cs
index 83d736b3fb..3e44b26738 100644
--- a/source/examples/generated/dotnet/Asymmetrics.snippet.asymmetry.cs
+++ b/source/examples/generated/dotnet/Asymmetrics.snippet.asymmetry.cs
@@ -1,11 +1,3 @@
-private partial class Measurement : IAsymmetricObject
-{
- [PrimaryKey, MapTo("_id")]
- public Guid Id { get; private set; } = Guid.NewGuid();
- public double Value { get; set; }
- public DateTimeOffset Timestamp { get; private set; } = DateTimeOffset.UtcNow;
-}
-
public void SendMeasurementToRealm()
{
var measurement = new Measurement
diff --git a/source/examples/generated/dotnet/Asymmetrics.snippet.configure-and-open-db.cs b/source/examples/generated/dotnet/Asymmetrics.snippet.configure-and-open-db.cs
new file mode 100644
index 0000000000..40ebbc5529
--- /dev/null
+++ b/source/examples/generated/dotnet/Asymmetrics.snippet.configure-and-open-db.cs
@@ -0,0 +1,6 @@
+var config = new FlexibleSyncConfiguration(user)
+{
+ Schema = new[] { typeof(Measurement) }
+};
+
+realm = Realm.GetInstance(config);
diff --git a/source/examples/generated/dotnet/Asymmetrics.snippet.connect-and-authenticate.cs b/source/examples/generated/dotnet/Asymmetrics.snippet.connect-and-authenticate.cs
new file mode 100644
index 0000000000..bc2989bbe5
--- /dev/null
+++ b/source/examples/generated/dotnet/Asymmetrics.snippet.connect-and-authenticate.cs
@@ -0,0 +1,3 @@
+App app = App.Create(myAppId);
+Realms.Sync.User user = app.LogInAsync(
+ Credentials.Anonymous()).Result;
diff --git a/source/examples/generated/dotnet/Asymmetrics.snippet.define-asymmetric-object.cs b/source/examples/generated/dotnet/Asymmetrics.snippet.define-asymmetric-object.cs
new file mode 100644
index 0000000000..e631673518
--- /dev/null
+++ b/source/examples/generated/dotnet/Asymmetrics.snippet.define-asymmetric-object.cs
@@ -0,0 +1,7 @@
+private partial class Measurement : IAsymmetricObject
+{
+ [PrimaryKey, MapTo("_id")]
+ public Guid Id { get; private set; } = Guid.NewGuid();
+ public double Value { get; set; }
+ public DateTimeOffset Timestamp { get; private set; } = DateTimeOffset.UtcNow;
+}
diff --git a/source/examples/generated/flutter/schemas.snippet.part-directive.dart b/source/examples/generated/flutter/schemas.snippet.part-directive.dart
index 730fd18b33..caf6dc542c 100644
--- a/source/examples/generated/flutter/schemas.snippet.part-directive.dart
+++ b/source/examples/generated/flutter/schemas.snippet.part-directive.dart
@@ -1 +1 @@
-part 'schemas.realm.dart';
+part 'modelFile.realm.dart';
diff --git a/source/examples/generated/java/sync/ClientResetTest.snippet.handle-manual-reset.java b/source/examples/generated/java/sync/ClientResetTest.snippet.handle-manual-reset.java
index d5c380d07c..660e86a0a1 100644
--- a/source/examples/generated/java/sync/ClientResetTest.snippet.handle-manual-reset.java
+++ b/source/examples/generated/java/sync/ClientResetTest.snippet.handle-manual-reset.java
@@ -40,7 +40,7 @@ public void handleManualReset(App app, SyncSession session, ClientResetRequiredE
Log.w("EXAMPLE", "Opened a fresh instance of the realm.");
- // Open the the realm backup -- as a dynamic realm
+ // Open the realm backup -- as a dynamic realm
// (no formal schema; access all data through field lookups)
DynamicRealm backupRealm = DynamicRealm.getInstance(error.getBackupRealmConfiguration());
Log.w("EXAMPLE", "Opened the backup realm.");
diff --git a/source/examples/generated/java/sync/ClientResetTest.snippet.handle-manual-reset.kt b/source/examples/generated/java/sync/ClientResetTest.snippet.handle-manual-reset.kt
index 872cd89cf9..c683e47afe 100644
--- a/source/examples/generated/java/sync/ClientResetTest.snippet.handle-manual-reset.kt
+++ b/source/examples/generated/java/sync/ClientResetTest.snippet.handle-manual-reset.kt
@@ -39,7 +39,7 @@ fun handleManualReset(app: App, session: SyncSession?, error: ClientResetRequire
}
Log.w("EXAMPLE", "Opened a fresh instance of the realm.")
- // Open the the realm backup -- as a dynamic realm
+ // Open the realm backup -- as a dynamic realm
// (no formal schema; access all data through field lookups)
val backupRealm =
DynamicRealm.getInstance(error.backupRealmConfiguration)
diff --git a/source/examples/generated/node/v12/formatted/geospatial.test.snippet.rql-geospatial.js.rst b/source/examples/generated/node/v12/formatted/geospatial.test.snippet.rql-geospatial.js.rst
new file mode 100644
index 0000000000..ac21e33fe0
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/geospatial.test.snippet.rql-geospatial.js.rst
@@ -0,0 +1,3 @@
+.. code-block:: javascript
+
+ "location geoWithin $0", smallCircle
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.aggregate-operators.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.aggregate-operators.ts.rst
new file mode 100644
index 0000000000..9aff639ec7
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.aggregate-operators.ts.rst
@@ -0,0 +1,20 @@
+.. code-block:: typescript
+
+ var priorityNum = 5;
+
+ // Find projects with average item `priority` above 5
+ "items.@avg.priority > $0", priorityNum
+
+ // Find projects where every item has a `priority` less than 5
+ "items.@max.priority < $0", priorityNum
+
+ // Find projects where every item has `priority` greater than 5
+ "items.@min.priority > $0", priorityNum
+
+ // Find projects with more than 5 items
+ "items.@count > $0", 5
+
+ // Find projects where the sum total value of `progressMinutes`
+ // across all items is greater than 100
+ "items.@sum.progressMinutes > $0",
+ 100
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.backlinks-atCount.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.backlinks-atCount.ts.rst
new file mode 100644
index 0000000000..74bd83b430
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.backlinks-atCount.ts.rst
@@ -0,0 +1,4 @@
+.. code-block:: typescript
+
+ // Find items that are not referenced by another object of any type
+ "@links.@count == 0"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.backlinks-atLinks.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.backlinks-atLinks.ts.rst
new file mode 100644
index 0000000000..7d2c811f88
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.backlinks-atLinks.ts.rst
@@ -0,0 +1,5 @@
+.. code-block:: typescript
+
+ // Find items that belong to a project with a quota less than 10
+ // (using '@links..')
+ "@links.Project.items.quota < 10"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.backlinks-collection-operators.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.backlinks-collection-operators.ts.rst
new file mode 100644
index 0000000000..8743becabc
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.backlinks-collection-operators.ts.rst
@@ -0,0 +1,23 @@
+.. code-block:: typescript
+
+ // Find items where no project that references the item has a
+ // quota greater than 10
+ "NONE @links.Project.items.quota > 10"
+
+ // Find items where all projects that reference the item have a
+ // quota less than 5
+ "ALL @links.Project.items.quota < 5"
+
+ // Find items that are referenced by multiple projects
+ "projects.@count > 1"
+
+ // Find items that are not referenced by any project
+ "@links.Project.items.@count == 0"
+
+ // Find items that belong to a project where the average item
+ // has been worked on for at least 10 minutes
+ "@links.Project.items.items.@avg.progressMinutes > 10"
+
+ // Find items that are not referenced by another object
+ // of any type (backlink count is 0)
+ "@links.@count == 0"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.backlinks-linkingObjects.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.backlinks-linkingObjects.ts.rst
new file mode 100644
index 0000000000..8e0bf24549
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.backlinks-linkingObjects.ts.rst
@@ -0,0 +1,6 @@
+.. code-block:: typescript
+
+ // Find items that belong to a project with a quota greater than 10
+ // through the Item object's `projects` property
+ // (using 'LinkingObjects')
+ "projects.quota > 10"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.basic-arithmetic.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.basic-arithmetic.ts.rst
new file mode 100644
index 0000000000..982f8c6f36
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.basic-arithmetic.ts.rst
@@ -0,0 +1,9 @@
+.. code-block:: typescript
+
+ // Evaluate against an item's `priority` property value:
+ "2 * priority > 6" // (resolves to `priority > 3`)
+
+ "priority >= 2 * (2 - 1) + 2" // (resolves to `priority >= 4`)
+
+ // Evaluate against multiple object property values:
+ "progressMinutes * priority == 90"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.comparison-operators.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.comparison-operators.ts.rst
new file mode 100644
index 0000000000..51e1eb6ada
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.comparison-operators.ts.rst
@@ -0,0 +1,13 @@
+.. code-block:: typescript
+
+ // Compare `priority` values against a threshold value
+ "priority > $0", 5
+
+ // Compare `assignee` values to `null` value.
+ "assignee == $0", null
+
+ // Compare `priority` values against an inclusive range of values
+ "priority BETWEEN { $0 , $1 }", 1, 5
+
+ // Compare `progressMinutes` values against any of the listed values
+ "progressMinutes IN { $0, $1, $2 }", 10, 30, 60
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.date-alt-representation.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.date-alt-representation.ts.rst
new file mode 100644
index 0000000000..9cfacf588a
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.date-alt-representation.ts.rst
@@ -0,0 +1,5 @@
+.. code-block:: typescript
+
+ var lastYear = new Date(1577883184000); // Unix timestamp in ms
+ var thisYear = new Date("2021-01-01@17:30:15:0"); // DateTime in UTC
+ var today = new Date("April 01, 2021 03:24:00"); // Alternate DateTime format
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.date-parameterized-query.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.date-parameterized-query.ts.rst
new file mode 100644
index 0000000000..539fa221c0
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.date-parameterized-query.ts.rst
@@ -0,0 +1,7 @@
+.. code-block:: typescript
+
+ // Find to-do items completed before today's date
+ "dateCompleted < $0", today
+
+ // Find to-do items completed between start of the year to today
+ "dateCompleted > $0 AND dateCompleted < $1", thisYear, today
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.deep-dot-notation.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.deep-dot-notation.ts.rst
new file mode 100644
index 0000000000..dcb91dfabc
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.deep-dot-notation.ts.rst
@@ -0,0 +1,5 @@
+.. code-block:: typescript
+
+ // Find projects whose `projectLocation` property contains an
+ // embedded Address object with a specific zip code
+ "projectLocation.address.zipcode == 10019"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.dictionary-operators.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.dictionary-operators.ts.rst
new file mode 100644
index 0000000000..186e16929b
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.dictionary-operators.ts.rst
@@ -0,0 +1,21 @@
+.. code-block:: typescript
+
+ // Find projects whose `comments` dictionary property
+ // have a key of 'status'
+ "comments.@keys == $0", "status"
+
+ // Find projects whose `comments` dictionary property
+ // have a 'status' key with a value that ends in 'track'
+ "comments['status'] LIKE $0", "*track"
+
+ // Find projects whose `comments` dictionary property
+ // have more than one key-value pair
+ "comments.@count > $0", 1
+
+ // Find projects whose `comments` dictionary property contains
+ // only values of type 'string'
+ "ALL comments.@type == 'string'"
+
+ // Find projects whose `comments` dictionary property contains
+ // no values of type 'int'
+ "NONE comments.@type == 'int'"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.dot-notation.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.dot-notation.ts.rst
new file mode 100644
index 0000000000..ecc72d500c
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.dot-notation.ts.rst
@@ -0,0 +1,5 @@
+.. code-block:: typescript
+
+ // Find projects whose `items` list property contains an item
+ // with a specific name
+ "items[0].name == 'Approve project plan'"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.equivalent-lists-any-operator.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.equivalent-lists-any-operator.ts.rst
new file mode 100644
index 0000000000..f38eb068a9
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.equivalent-lists-any-operator.ts.rst
@@ -0,0 +1,5 @@
+.. code-block:: typescript
+
+ "assignee == ANY { $0, $1 }", "Alex", "Ali"
+
+ "assignee == { $0, $1 }", "Alex", "Ali" // Equivalent (ANY is implied)
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.list-comparisons-collection.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.list-comparisons-collection.ts.rst
new file mode 100644
index 0000000000..f484e1de24
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.list-comparisons-collection.ts.rst
@@ -0,0 +1,5 @@
+.. code-block:: typescript
+
+ // Find an item with the specified ObjectId value
+ // in the`items` collection
+ "oid(631a072f75120729dc9223d9) IN items._id"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.list-comparisons-parameterized.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.list-comparisons-parameterized.ts.rst
new file mode 100644
index 0000000000..5dc01354ae
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.list-comparisons-parameterized.ts.rst
@@ -0,0 +1,10 @@
+.. code-block:: typescript
+
+ const ids = [
+ new BSON.ObjectId("631a072f75120729dc9223d9"),
+ new BSON.ObjectId("631a0737c98f89f5b81cd24d"),
+ new BSON.ObjectId("631a073c833a34ade21db2b2"),
+ ];
+ // Find items with an ObjectId value matching any value
+ // in the parameterized list
+ const parameterizedQuery = realm.objects(Item).filtered("_id IN $0", ids);
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.list-comparisons-static.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.list-comparisons-static.ts.rst
new file mode 100644
index 0000000000..95e23c3fc5
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.list-comparisons-static.ts.rst
@@ -0,0 +1,5 @@
+.. code-block:: typescript
+
+ // Find items with a priority value matching any value
+ // in the static list
+ "priority IN {0, 1, 2}"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.logical-operators.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.logical-operators.ts.rst
new file mode 100644
index 0000000000..817b4fc9ea
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.logical-operators.ts.rst
@@ -0,0 +1,7 @@
+.. code-block:: typescript
+
+ // Find all items assigned to Ali AND marked completed
+ "assignee == $0 AND isComplete == $1", "Ali", true
+
+ // Find all items assigned to Alex OR to Ali
+ "assignee == $0 OR assignee == $1", "Alex", "Ali"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.nil-type.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.nil-type.ts.rst
new file mode 100644
index 0000000000..8c9df22d24
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.nil-type.ts.rst
@@ -0,0 +1,4 @@
+.. code-block:: typescript
+
+ "assignee == nil"
+ "assignee == $0", null // 'null' maps to SDK language's null pointer
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.oid.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.oid.ts.rst
new file mode 100644
index 0000000000..d8865fd19d
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.oid.ts.rst
@@ -0,0 +1,17 @@
+.. code-block:: typescript
+
+ // Find an item whose `_id` matches the ObjectID value
+ // passed to 'oid()'
+ "_id == oid(6001c033600510df3bbfd864)"
+
+ // Find an item whose `_id` matches the ObjectID passed as
+ // a parameterized query argument
+ "_id == $0", oidValue
+
+ // Find an item whose `id` matches the UUID value
+ // passed to 'uuid()'
+ "id == uuid(d1b186e1-e9e0-4768-a1a7-c492519d47ee)"
+
+ // Find an item whose `_id` matches the UUID passed as
+ // a parameterized query argument
+ "id == $0", uuidValue
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.parameterized-query.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.parameterized-query.ts.rst
new file mode 100644
index 0000000000..69598f1ef6
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.parameterized-query.ts.rst
@@ -0,0 +1,8 @@
+.. code-block:: typescript
+
+ // Include one parameter with `$0`
+ "progressMinutes > 1 AND assignee == $0", "Ali"
+
+ // Include multiple parameters using ascending integers,
+ // starting at`$0`
+ "progressMinutes > $0 AND assignee == $1", 1, "Alex"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.predicate.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.predicate.ts.rst
new file mode 100644
index 0000000000..49e38c91c0
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.predicate.ts.rst
@@ -0,0 +1,6 @@
+.. code-block:: typescript
+
+ "priority == 1"
+ // Property Name: priority
+ // Operator: ==
+ // Value: 1
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.rql-fts.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.rql-fts.ts.rst
new file mode 100644
index 0000000000..7efe0eeca7
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.rql-fts.ts.rst
@@ -0,0 +1,12 @@
+.. code-block:: typescript
+
+ // Find items with 'write' in the name.
+ "name TEXT $0", "write"
+
+ // Use '-' to exclude:
+ // Find items with 'write' but not 'tests' in the name
+ "name TEXT $0", "write -tests"
+
+ // Use '*' to match any characters after a prefix:
+ // Find items with a name that starts with 'wri'
+ "name TEXT $0", "wri*"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.serialized-query.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.serialized-query.ts.rst
new file mode 100644
index 0000000000..e51d4723e7
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.serialized-query.ts.rst
@@ -0,0 +1,3 @@
+.. code-block:: typescript
+
+ "progressMinutes > 1 AND assignee == 'Ali'"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.set-operators.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.set-operators.ts.rst
new file mode 100644
index 0000000000..f9fddbb007
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.set-operators.ts.rst
@@ -0,0 +1,17 @@
+.. code-block:: typescript
+
+ // Find projects with no complete items
+ "NONE items.isComplete == $0", true
+
+ // Find projects that contain any item with priority 10
+ "items.priority == $0", 10 // (ANY operator is implied)
+
+ // Find projects that only contain completed items
+ "ALL items.isComplete == $0", true
+
+ // Find projects with at least one item assigned to
+ // either Alex or Ali
+ "ANY items.assignee IN { $0 , $1 }", "Alex", "Ali"
+
+ // Find projects with no items assigned to either Alex or Ali
+ "NONE items.assignee IN { $0 , $1 }", "Alex", "Ali"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.simple-query.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.simple-query.ts.rst
new file mode 100644
index 0000000000..9b77d1e44a
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.simple-query.ts.rst
@@ -0,0 +1,5 @@
+.. code-block:: typescript
+
+ const items = realm.objects(Item);
+ // Get all items where 'priority' property is 7 or more
+ const importantItems = items.filtered("priority >= $0", 7);
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.sort-distinct-limit-order-matters.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.sort-distinct-limit-order-matters.ts.rst
new file mode 100644
index 0000000000..68917c33fb
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.sort-distinct-limit-order-matters.ts.rst
@@ -0,0 +1,11 @@
+.. code-block:: typescript
+
+ // 1. Sorts by highest priority
+ // 2. Returns the first item
+ // 3. Remove duplicate names (N/A - a single item is always distinct)
+ "assignee == null SORT(priority ASC) LIMIT(1) DISTINCT(name)"
+
+ // 1. Removes any duplicates by name
+ // 2. Sorts by highest priority
+ // 3. Returns the first item
+ "assignee == null DISTINCT(name) SORT(priority ASC) LIMIT(1)"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.sort-distinct-limit.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.sort-distinct-limit.ts.rst
new file mode 100644
index 0000000000..5860a87bf8
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.sort-distinct-limit.ts.rst
@@ -0,0 +1,13 @@
+.. code-block:: typescript
+
+ // Find incomplete items, sort by `priority` in descending order,
+ // then sort equal `priority` values by `progressMinutes`
+ // in ascending order
+ "isComplete == false SORT(priority DESC, progressMinutes ASC)"
+
+ // Find high priority items, then remove from the results any items
+ // with duplicate values for both `name` AND `assignee` properties
+ "priority >= 5 DISTINCT(name, assignee)"
+
+ // Find in-progress items, then return the first 10 results
+ "progressMinutes > 0 && isComplete != true LIMIT(10)"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.string-operators.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.string-operators.ts.rst
new file mode 100644
index 0000000000..3731055a67
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.string-operators.ts.rst
@@ -0,0 +1,17 @@
+.. code-block:: typescript
+
+ // Find projects whose name starts with 'E' or 'e'
+ // (case-insensitive query)
+ "name BEGINSWITH[c] $0", "E"
+
+ // Find projects whose name contains 'ie'
+ // (case-sensitive query)
+ "name CONTAINS $0", "ie"
+
+ // Find items where the assignee name is lexicographically between
+ // 'Ali' and 'Chris'(case-sensitive)
+ "assignee BETWEEN { $0 , $1 }", "Ali", "Chris"
+
+ // Find projects where the street address is lexicographically
+ // greater than '123 Main St'(case-sensitive)
+ "projectLocation.address.street > $0", "123 Main St"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.subquery.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.subquery.ts.rst
new file mode 100644
index 0000000000..53e6b7e2ac
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.subquery.ts.rst
@@ -0,0 +1,8 @@
+.. code-block:: typescript
+
+ // Find projects with incomplete items with 'Demo' in the name
+ "SUBQUERY(items, $item, $item.isComplete == false AND $item.name CONTAINS[c] 'Demo').@count > 0"
+
+ // Find projects where the number of completed items is
+ // greater than or equal to the project's `quota` property
+ "SUBQUERY(items, $item, $item.isComplete == true).@count >= quota"
diff --git a/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.type-operator.ts.rst b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.type-operator.ts.rst
new file mode 100644
index 0000000000..f321ed7245
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/realm-query-language.test.snippet.type-operator.ts.rst
@@ -0,0 +1,12 @@
+.. code-block:: typescript
+
+ // Find projects with an `additionalInfo` property of string type
+ "additionalInfo.@type == 'string'"
+
+ // Find projects with an `additionalInfo` property of
+ // `collection` type, which matches list or dictionary types
+ "additionalInfo.@type == 'collection'"
+
+ // Find projects with an `additionalInfo` property of list type,
+ // where any list element is of type 'bool'
+ "additionalInfo[*].@type == 'bool'"
diff --git a/source/examples/generated/node/v12/formatted/rql-data-models.snippet.rql-data-models.js.rst b/source/examples/generated/node/v12/formatted/rql-data-models.snippet.rql-data-models.js.rst
new file mode 100644
index 0000000000..af738f729f
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/rql-data-models.snippet.rql-data-models.js.rst
@@ -0,0 +1,33 @@
+.. code-block:: javascript
+
+ const Item = {
+ name: "Item",
+ properties: {
+ _id: "objectId",
+ name: { type: "string", indexed: "full-text" },
+ isComplete: { type: "bool", default: false },
+ assignee: "string?",
+ priority: { type: "int", default: 0 },
+ progressMinutes: { type: "int", default: 0 },
+ projects: {
+ type: "linkingObjects",
+ objectType: "Project",
+ property: "items",
+ },
+ },
+ primaryKey: "_id",
+ };
+
+ const Project = {
+ name: "Project",
+ properties: {
+ _id: "objectId",
+ name: "string",
+ items: "Item[]",
+ quota: "int?",
+ comments: "string?{}",
+ projectLocation: "Office?",
+ additionalInfo: "mixed",
+ },
+ primaryKey: "_id",
+ };
diff --git a/source/examples/generated/node/v12/formatted/rql-data-models.snippet.rql-data-models.ts.rst b/source/examples/generated/node/v12/formatted/rql-data-models.snippet.rql-data-models.ts.rst
new file mode 100644
index 0000000000..4c74ff4715
--- /dev/null
+++ b/source/examples/generated/node/v12/formatted/rql-data-models.snippet.rql-data-models.ts.rst
@@ -0,0 +1,51 @@
+.. code-block:: typescript
+
+ export class Item extends Realm.Object
- {
+ _id!: BSON.ObjectId;
+ name!: string;
+ isComplete!: boolean;
+ assignee?: string;
+ priority!: number;
+ progressMinutes?: number;
+
+ static schema: ObjectSchema = {
+ name: "Item",
+ properties: {
+ _id: "objectId",
+ name: { type: "string", indexed: "full-text" },
+ isComplete: { type: "bool", default: false },
+ assignee: "string?",
+ priority: { type: "int", default: 0 },
+ progressMinutes: { type: "int", default: 0 },
+ projects: {
+ type: "linkingObjects",
+ objectType: "Project",
+ property: "items",
+ },
+ },
+ primaryKey: "_id",
+ };
+ }
+ export class Project extends Realm.Object {
+ _id!: BSON.ObjectId;
+ name!: string;
+ items!: Realm.List
- ;
+ quota?: number;
+ comments?: Realm.Dictionary;
+ projectLocation?: Office;
+ additionalInfo!: Realm.Mixed;
+
+ static schema: ObjectSchema = {
+ name: "Project",
+ properties: {
+ _id: "objectId",
+ name: "string",
+ items: "Item[]",
+ quota: "int?",
+ comments: "string?{}",
+ projectLocation: "Office?",
+ additionalInfo: "mixed",
+ },
+ primaryKey: "_id",
+ };
+ }
diff --git a/source/examples/generated/react/providers-hooks.snippet.use-providers.js b/source/examples/generated/react/providers-hooks.snippet.use-providers.js
new file mode 100644
index 0000000000..c6f6567bb6
--- /dev/null
+++ b/source/examples/generated/react/providers-hooks.snippet.use-providers.js
@@ -0,0 +1,15 @@
+import React from "react";
+import { APP_ID } from "../realm.config.json";
+import { AppProvider, UserProvider, RealmProvider, useRealm, useUser } from "@realm/react";
+
+export const AppWrapper = () => {
+ return (
+ {/* pass in your App ID as a prop */}
+
+
+ {/* call any app components here */}
+
+
+
+ );
+ };
diff --git a/source/frameworks.txt b/source/frameworks.txt
new file mode 100644
index 0000000000..baa81b32cf
--- /dev/null
+++ b/source/frameworks.txt
@@ -0,0 +1,34 @@
+.. _sdks-build-with-frameworks:
+
+=====================
+Build with Frameworks
+=====================
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+.. toctree::
+ :titlesonly:
+
+ React
+ React Native
+ Electron
+ Flutter
+ Maui
+ .NET
+ SwiftUI
+
+The following pages contain information about building with specific
+frameworks using Atlas Device SDK:
+
+- :ref:`sdks-build-with-react`
+- :ref:`sdks-build-with-react-native`
+- :ref:`sdks-build-with-electron`
+- :ref:`sdks-build-with-flutter`
+- :ref:`sdks-build-with-maui`
+- :ref:`sdks-build-with-dotnet`
+- :ref:`sdks-build-with-react-native`
+- :ref:`sdks-build-with-swiftui`
\ No newline at end of file
diff --git a/source/frameworks/dotnet.txt b/source/frameworks/dotnet.txt
new file mode 100644
index 0000000000..fb184051fc
--- /dev/null
+++ b/source/frameworks/dotnet.txt
@@ -0,0 +1,14 @@
+.. _sdks-build-with-dotnet:
+
+===============
+Build with .NET
+===============
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Placeholder page for information about building with .NET. (This may
+be a directory depending on how much content we have/need.)
diff --git a/source/frameworks/electron.txt b/source/frameworks/electron.txt
new file mode 100644
index 0000000000..f6f7c4f711
--- /dev/null
+++ b/source/frameworks/electron.txt
@@ -0,0 +1,14 @@
+.. _sdks-build-with-electron:
+
+===================
+Build with Electron
+===================
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Placeholder page for information about building with Electron. (This may
+be a directory as we have two pages for this?)
diff --git a/source/frameworks/flutter.txt b/source/frameworks/flutter.txt
new file mode 100644
index 0000000000..a2cd360fcf
--- /dev/null
+++ b/source/frameworks/flutter.txt
@@ -0,0 +1,11 @@
+.. _sdks-build-with-flutter:
+
+==================
+Build with Flutter
+==================
+
+.. toctree::
+ :titlesonly:
+
+ Install
+ Quick Start
diff --git a/source/frameworks/flutter/install.txt b/source/frameworks/flutter/install.txt
new file mode 100644
index 0000000000..e01371658d
--- /dev/null
+++ b/source/frameworks/flutter/install.txt
@@ -0,0 +1,154 @@
+.. _sdks-install-sdk-in-flutter-project:
+
+====================================
+Install Atlas Device SDK for Flutter
+====================================
+
+.. meta::
+ :description: Install the Atlas Device SDK in a Flutter application.
+ :keywords: Realm, Flutter SDK, code example
+
+.. facet::
+ :name: genre
+ :values: tutorial
+
+.. facet::
+ :name: programming_language
+ :values: dart
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Use Atlas Device SDK in a Flutter project to build cross-platform
+applications with Flutter and Dart. This guide provides instructions for
+installing the SDK in a Flutter project. For details about installing the SDK
+in a standalone Dart project, refer to
+:ref:`sdks-install`.
+
+Requirements
+------------
+
+Install Flutter with Dart in your development environment. The Flutter
+installation includes Dart. To learn how, refer to the official
+`Flutter Installation Guide `__.
+
+When building a project with Flutter, the SDK requires the following minimum
+versions:
+
+- Flutter version {+flutter-minimum-version+} or later.
+- Dart version {+dart-minimum-version+} or later.
+
+Supported Platforms
+~~~~~~~~~~~~~~~~~~~
+
+.. include:: /includes/api-details/dart/install/install-supported-platforms-description.rst
+
+Install the SDK
+---------------
+
+.. procedure::
+
+ .. step:: Create a Project
+
+ To create a Flutter project, run the following commands:
+
+ .. code-block:: bash
+
+ flutter create
+ cd
+
+ For more information, refer to Flutter's `Get Started Guide
+ `__.
+
+ .. step:: Add the SDK to the Project
+
+ To add the Flutter SDK to your project, run the following command:
+
+ .. code-block:: bash
+
+ flutter pub add realm
+
+ This downloads the `realm `__
+ package, and adds it to your project.
+
+ In your ``pubspec.yaml`` file, you should see:
+
+ .. code-block:: yaml
+ :caption: pubspec.yaml
+
+ dependencies:
+ realm:
+
+ .. note:: Using Networking in your macOS App
+
+ If you are developing with Flutter in the macOS App Sandbox and
+ require network access, you must enable network entitlements in your
+ app. By default, network requests are not allowed due to built-in
+ macOS security settings.
+
+ To learn how to change your app's macOS entitlements, refer to
+ :ref:`Device Sync and App Sandbox Entitlements
+ `.
+
+Update the Package Version
+--------------------------
+
+To change the version of the SDK in your project, perform the following steps:
+
+.. procedure::
+
+ .. step:: Update the ``pubspec.yaml`` File
+
+ Update the package version in your :file:`pubspec.yaml` file dependencies.
+
+ .. code-block:: yaml
+ :caption: pubspec.yaml
+
+ dependencies:
+ realm:
+
+ .. step:: Install the Updated Package
+
+ From the command line, run the following command to install the updated
+ version:
+
+ .. code-block::
+
+ dart pub upgrade realm
+
+ Then, run the following command to install the updated SDK's native
+ binaries:
+
+ .. code-block::
+
+ dart run realm install
+
+ .. step:: Regenerate Object Models
+
+ Changes to the package version may affect the functionality of the object
+ models. From the command line, run the following command to regenerate
+ object models with new and updated functionality:
+
+ .. code-block:: bash
+
+ dart run realm generate
+
+ .. include:: /includes/flutter-v2-breaking-change.rst
+
+Import the SDK
+--------------
+
+To use the SDK in your app, import the package into any files that use it:
+
+.. code-block:: dart
+ :caption: ExampleFile.dart
+
+ import 'package:realm/realm.dart';
+
+Platform-Specific Considerations
+--------------------------------
+
+.. include:: /includes/api-details/dart/install/install-platform-specific-considerations-description.rst
diff --git a/source/frameworks/flutter/quick-start.txt b/source/frameworks/flutter/quick-start.txt
new file mode 100644
index 0000000000..c28d39641e
--- /dev/null
+++ b/source/frameworks/flutter/quick-start.txt
@@ -0,0 +1,110 @@
+.. _frameworks-flutter-quick-start:
+
+========================
+Quick Start with Flutter
+========================
+
+.. meta::
+ :description: Get started using Atlas Device SDK with Flutter in a Flutter application.
+ :keywords: Realm, Flutter, Flutter SDK, code example
+
+.. facet::
+ :name: genre
+ :values: tutorial
+
+.. facet::
+ :name: programming_language
+ :values: dart
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+This quick start demonstrates how to use Atlas Device SDK with Flutter in a
+Flutter application.
+
+.. tip:: Flutter Project or Standalone Dart Project?
+
+ This quick start contains information for using the SDK in a Flutter
+ project. The package that you import and the way you create object
+ models differs when using the SDK in a standalone Dart project. For
+ a quick start using a standalone Dart project, refer to
+ :ref:`sdks-quick-start`.
+
+Install the SDK
+---------------
+
+Install the ``realm`` package for use in Flutter applications. For more
+information about installing the SDK in a Flutter project, refer to
+:ref:`sdks-install`.
+
+Import the SDK
+--------------
+
+Import the ``realm`` package into any files where you use it.
+
+.. code-block:: dart
+ :caption: ExampleFile.dart
+
+ import 'package:realm/realm.dart';
+
+Define Your Object Model
+------------------------
+
+Your application's **data model** defines the structure of data stored within
+the database. You can define your application's data model via Dart
+classes in your application code with an SDK object schema.
+You then have to generate the :flutter-sdk:`RealmObjectBase `
+class that's used within your application.
+
+For more information, refer to :ref:`Define an Object Model
+`.
+
+.. procedure::
+
+ .. step:: Create a Model Class
+
+ Add an SDK model class. Give your class a private name
+ (starting with ``_``), such as a file ``car.dart`` with a class
+ ``_Car``.
+
+ .. literalinclude:: /examples/generated/flutter/car.snippet.define-model-flutter.dart
+ :language: dart
+ :caption: car.dart
+
+ .. step:: Generate an SDK Object Class
+
+ Generate a RealmObject class ``Car`` from the data model class ``_Car``:
+
+ .. code-block::
+
+ dart run realm generate
+
+ Running this creates a ``Car`` class in a ``car.realm.dart`` file
+ located in the directory where you defined the model class. This ``Car``
+ class is public and part of the same library as the ``_Car`` data model
+ class. The generated ``Car`` class is what's used throughout your
+ application.
+
+ .. step:: Watch for Changes to the Model (Optional)
+
+ You can watch your data model class to generate a new ``Car`` class
+ whenever there's a change to ``_Car``:
+
+ .. code-block::
+
+ dart run realm generate --watch
+
+Perform CRUD Operations and More
+--------------------------------
+
+Installing the library and the commands to generate the models are specific
+to using the SDK with a Flutter project or a standalone Dart project. But
+all the other operations, from reading and writing data to syncing data across
+devices, are the same for a Flutter or standalone Dart project.
+
+To learn more about performing these operations, refer to the main Quick Start.
+Shared content starts with the :ref:`Open a Database `
+section.
diff --git a/source/frameworks/maui.txt b/source/frameworks/maui.txt
new file mode 100644
index 0000000000..468ebfb15d
--- /dev/null
+++ b/source/frameworks/maui.txt
@@ -0,0 +1,14 @@
+.. _sdks-build-with-maui:
+
+===============
+Build with Maui
+===============
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Placeholder page for information about building with Maui. (This may
+be a directory depending on how much content we have/need.)
diff --git a/source/frameworks/react-native.txt b/source/frameworks/react-native.txt
new file mode 100644
index 0000000000..446dbdd788
--- /dev/null
+++ b/source/frameworks/react-native.txt
@@ -0,0 +1,16 @@
+.. _sdks-build-with-react-native:
+
+=======================
+Build with React Native
+=======================
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Placeholder page for information about building with React Native
+and ``@realm/react``.
+
+This will be a directory with content.
diff --git a/source/frameworks/react.txt b/source/frameworks/react.txt
new file mode 100644
index 0000000000..910f2507ff
--- /dev/null
+++ b/source/frameworks/react.txt
@@ -0,0 +1,30 @@
+.. _sdks-build-with-react:
+
+================
+Build with React
+================
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+.. toctree::
+ :titlesonly:
+
+ Install
+ Quick Start
+ Providers & Hooks
+ Model Data
+ Open & Manage Database Files
+ Read & Write Data
+ React to Changes
+ Access Atlas
+ Manage Users
+ Sync Data
+ Test & Debug
+ API Reference
+
+Placeholder page for information about building with React. (This may
+be a directory depending on how much content we have/need.)
\ No newline at end of file
diff --git a/source/frameworks/react/access-atlas.txt b/source/frameworks/react/access-atlas.txt
new file mode 100644
index 0000000000..49711e60a3
--- /dev/null
+++ b/source/frameworks/react/access-atlas.txt
@@ -0,0 +1,14 @@
+.. _sdks-react-access-atlas:
+
+============
+Access Atlas
+============
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Placeholder page for information about building with @realm/react. (This may
+be a directory as we have two pages for this?)
\ No newline at end of file
diff --git a/source/frameworks/react/api-reference.txt b/source/frameworks/react/api-reference.txt
new file mode 100644
index 0000000000..9440f53fcb
--- /dev/null
+++ b/source/frameworks/react/api-reference.txt
@@ -0,0 +1,10 @@
+.. _sdks-react-api-reference:
+
+===================================
+API Reference
+===================================
+
+.. toctree::
+ :titlesonly:
+
+ JavaScript SDK Reference (@realm)
\ No newline at end of file
diff --git a/source/frameworks/react/install.txt b/source/frameworks/react/install.txt
new file mode 100644
index 0000000000..0259df8e69
--- /dev/null
+++ b/source/frameworks/react/install.txt
@@ -0,0 +1,14 @@
+.. _sdks-react-install:
+
+===================================
+Install @realm/react in a React App
+===================================
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Placeholder page for information about building with @realm/react. (This may
+be a directory as we have two pages for this?)
\ No newline at end of file
diff --git a/source/frameworks/react/manage-database-files.txt b/source/frameworks/react/manage-database-files.txt
new file mode 100644
index 0000000000..dd06373449
--- /dev/null
+++ b/source/frameworks/react/manage-database-files.txt
@@ -0,0 +1,14 @@
+.. _sdks-react-manage-database-files:
+
+============================
+Open & Manage Database Files
+============================
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Placeholder page for information about building with @realm/react. (This may
+be a directory as we have two pages for this?)
\ No newline at end of file
diff --git a/source/frameworks/react/manage-users.txt b/source/frameworks/react/manage-users.txt
new file mode 100644
index 0000000000..4c775c3b3f
--- /dev/null
+++ b/source/frameworks/react/manage-users.txt
@@ -0,0 +1,14 @@
+.. _sdks-react-manage-users:
+
+============
+Manage Users
+============
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Placeholder page for information about building with @realm/react. (This may
+be a directory as we have two pages for this?)
\ No newline at end of file
diff --git a/source/frameworks/react/model-data.txt b/source/frameworks/react/model-data.txt
new file mode 100644
index 0000000000..d9451a5f84
--- /dev/null
+++ b/source/frameworks/react/model-data.txt
@@ -0,0 +1,14 @@
+.. _sdks-react-model-data:
+
+==========
+Model Data
+==========
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Placeholder page for information about building with @realm/react. (This may
+be a directory as we have two pages for this?)
\ No newline at end of file
diff --git a/source/frameworks/react/providers-hooks.txt b/source/frameworks/react/providers-hooks.txt
new file mode 100644
index 0000000000..f70976a96a
--- /dev/null
+++ b/source/frameworks/react/providers-hooks.txt
@@ -0,0 +1,777 @@
+.. _sdks-react-providers-hooks:
+
+=================
+Providers & Hooks
+=================
+
+.. meta::
+ :description: Develop apps using the providers and Hooks available in the @realm/react library.
+ :keywords: Realm, JavaScript SDK, React, code example
+
+.. facet::
+ :name: genre
+ :values: reference
+
+.. facet::
+ :name: programming_language
+ :values: javascript/typescript
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 3
+ :class: singlecol
+
+The ``@realm/react`` library offers custom React components that eliminate the boilerplate needed to
+develop a React app using JavaScript-based Atlas Device SDKs. The components leverage the
+Provider design pattern to manage your Atlas connection, user creation and authentication, and
+data management.
+
+- ``AppProvider``: Used to connect to your :ref:`Atlas App Services App Client
+ `, including for user authentication.
+
+- ``UserProvider``: Used to access the logged-in :ref:`user object `.
+
+- ``RealmProvider``: Used to work with the configured database.
+
+Use these providers to develop with frameworks and for platforms that support a React
+environment, including: React Native, Web, and Electron.
+
+Getting Started with Providers
+------------------------------
+
+Like all React components, you call providers using HTML opening and closing tags. Nesting a
+component within another component's tags creates a parent-child relationship between them,
+where child components can access the context created by their parent component.
+
+Context refers to the information made accessible by a provider. For example, the
+``RealmProvider`` context is the Realm database it connects to, so all components nested in the
+``RealmProvider`` have access to that database.
+
+Props
+~~~~~
+
+Components take parameters called props as input, passed into the opening tag. The props passed
+into a parent component help create the context inherited by the components it wraps. Each
+Provider has different props you can use for configuration.
+
+.. tabs::
+
+ .. tab:: RealmProvider Props
+ :tabid: realm-provider-props
+
+ All properties of :realm-react-sdk:`BaseConfiguration ` can be passed as props.
+
+ The most common BaseConfiguration properties used are:
+
+ - ``schema?: (RealmObjectConstructor | ObjectSchema)[]``
+
+ Specifies all the object schemas in the database. Required when first creating a database.
+ If omitted, the schema will be read from the existing database file.
+
+ - ``sync?: SyncConfiguration``
+
+ Defines Device Sync behavior for this database, including initial subscriptions.
+
+ ``RealmProvider`` has more props that define its behavior:
+
+ - ``fallback?: React.ComponentType | React.ReactElement | null | undefined``
+
+ The fallback component to render while the database is opening or if it fails to open.
+
+ - ``closeOnUnmount?: boolean``
+
+ Default is ``true``. If set to ``false``, the open database will not close when the
+ component unmounts.
+
+ - ``realmRef?: React.MutableRefObject``
+
+ A ref to the database instance. This is useful if you need to access the database
+ instance outside of the scope of the database.
+
+ - ``children: React.ReactNode``
+
+ - ``realm?: Realm``
+
+ A configured Realm instance for the provider to connect to. Using this prop
+ eliminates the need to pass in other configuration parameters as props.
+
+ ``closeOnUnmount`` will be set to ``false`` by default.
+
+ For more details, see the :realm-react-sdk:`RealmProvider API Reference (@realm/react) `
+
+ .. tab:: AppProvider Props
+ :tabid: app-provider-props
+
+ All properties of :realm-react-sdk:`AppConfiguration
+ ` can be passed as props to ``AppProvider``.
+
+ The most common AppConfiguration property used is:
+
+ - ``id: string``
+
+ Specifies the App Services App ID.
+
+ ``AppProvider`` has more an additional prop that defines its behavior:
+
+ - ``app?: Realm.App``
+
+ An Atlas App Services App for the provider to connect to. Using this prop
+ eliminates the need to pass in other configuration parameters as props.
+
+ For more details, see the :realm-react-sdk:`AppProvider API Reference (@realm/react) `
+
+ .. tab:: UserProvider Props
+ :tabid: user-provider-props
+
+ The only prop passed into ``UserProvider`` is an optional fallback component:
+
+ - ``fallback?: React.ComponentType | React.ReactElement | null | undefined``
+
+ The fallback component to render if there is no authorized user. This can be
+ used to render a log in screen or otherwise handle authentication.
+
+ For more details, see the :realm-react-sdk:`UserProvider API Reference (@realm/react) `
+
+Configure your Providers
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Your app's needs determine what providers you use, as all three providers are not always
+necessary. The example below walks through configuring all three providers. If your app does not need a
+certain provider, you can skip the steps and omit the code for that provider.
+
+**To configure your providers:**
+
+1. Import ``RealmProvider``, ``AppProvider``, and ``UserProvider`` from ``@realm/react``.
+
+2. Configure ``AppProvider``.
+
+ a. Pass your App Services App ID string to the ``id`` prop of the ``AppProvider``.
+
+ b. Add other configuration object properties as props to ``AppProvider`` as needed.
+
+3. Configure ``UserProvider`` and nest it within ``AppProvider``.
+
+ a. Pass a component that logs in a user to the ``fallback`` prop. The app renders this component if there is no authenticated user.
+
+4. Configure ``RealmProvider`` and nest it within ``UserProvider``.
+
+ a. Pass your object models to the ``schema`` prop.
+
+ b. If configuring a synced database, use the ``sync`` prop. Sync properties are defined by
+ a :ref:`Sync Config Object `.
+
+ To sync data, you must set up a sync subscription. The example below uses an initial subscription,
+ but you can also set up subscriptions in ``RealmProvider`` child components.
+
+ c. Add other configuration object properties as props to ``RealmProvider`` as needed.
+
+5. Nest your app components in the providers.
+
+ Once your providers have been configured and nested as described above, nest your app components within the
+ provider whose context it needs to access. Generally, you can nest your components within the
+ ``RealmProvider`` to ensure they have access to all three providers' contexts.
+
+The rendering of each component is dependent on the successful
+execution of its parent components' functionality. For example, while ``AppProvider`` is
+connecting to your app's App Services backend, or if ``AppProvider`` is unable to connect, the
+components it wraps will not render. In these cases, the provider's fallback component is
+rendered instead.
+
+You *must* nest the providers as shown below to ensure each has access to its required context:
+
+.. literalinclude:: /examples/generated/react-native/ts/configure-realm-sync.test.snippet.configure-realm-sync-full.tsx
+ :language: javascript
+
+.. note:: Exposing more than one database using createRealmContext()
+
+ The example above details how to configure and expose a single database using a ``RealmProvider``
+ imported directly from ``@realm/react``.
+ For information about using ``createRealmContext()`` to configure a database or exposing more
+ than one database, see :ref:`Create Context with createRealmContext()
+ ` below.
+
+Working with Providers using Hooks
+----------------------------------
+
+To use the provider's context in your app's components, you can use Hooks.
+
+Hooks act as functions used to access states in your app. React offers built-in Hooks you can
+use either on their own or to build custom Hooks.
+
+There are two important rules to consider when working with Hooks:
+
+- Hooks can only be used at the top level of a React component.
+- Hooks can only be called in a React component or a custom Hook, not in regular JavaScript
+ functions.
+
+The ``@realm/react`` library has custom Hooks for each provider you can
+use to access a provider's context in the components it wraps.
+
+RealmProvider Hooks
+~~~~~~~~~~~~~~~~~~~
+
+.. _sdks-react-use-realm-hook:
+
+useRealm()
+``````````
+
+.. code:: typescript
+ :copyable: false
+ :caption: Type signature
+
+ useRealm(): Realm
+
+The ``useRealm()`` Hook returns an opened :js-sdk:`database instance`. The database instance
+gives you access to database methods and properties. For example, you can call
+``realm.write()`` to add a Realm Object to your database.
+
+To learn more about modifying data in your database, refer to :ref:`Write Transactions
+`.
+
+.. literalinclude:: /examples/generated/react-native/ts/create-test.test.snippet.crud-create-object.tsx
+ :language: typescript
+ :emphasize-lines: 3, 6-8, 15
+
+*Returns*
+
+- ``Realm`` Returns a database instance from the ``RealmProvider`` context.
+
+.. _sdks-react-use-object-hook:
+
+useObject()
+```````````
+
+.. code:: typescript
+ :copyable: false
+ :caption: Type signature
+
+ useObject(type, sdks-open-synced-database-offlineKey): T & Realm.Object | null
+
+The ``useObject()`` Hook returns a :js-sdk:`Realm Object ` for a given
+:ref:`primary key `. You can pass an object class
+or the class name as a string and the primary key.
+
+The ``useObject()`` Hook returns ``null`` if the object doesn't exist or you have
+deleted it. The Hook will automatically subscribe to updates and rerender the
+component using the Hook on any change to the object.
+
+.. literalinclude:: /examples/generated/react-native/ts/read.test.snippet.crud-read-object-by-id.tsx
+ :language: typescript
+
+*Parameters*
+
+- ``type: string`` A string that matches your object model's class name or a reference to a
+ class that extends :js-sdk:`Realm.Object `.
+- ``primaryKey: T[keyof T]`` The primary key of the desired object.
+
+*Returns*
+
+- ``Realm.Object | null`` A Realm Object or ``null`` if no object is found.
+
+.. _sdks-react-use-query-hook:
+
+useQuery()
+``````````
+
+.. code:: typescript
+ :copyable: false
+ :caption: Type signature
+
+ useQuery(type, query?, deps?): Realm.Results>
+
+The ``useQuery()`` Hook returns a :js-sdk:`result ` that is a
+collection of Realm Objects of a given type. These are the results of your query. A query can
+be an object class or the class name as a string.
+
+The ``useQuery()`` method subscribes to updates to any objects in the collection
+and rerenders the component using it on any change to the results.
+
+You can use ``.filtered()`` and ``.sorted()`` to filter and sort your query
+results. Do this in the ``query`` argument of ``useQuery`` so that
+they only run when there are changes in the dependency array. For more examples,
+refer to the :ref:`Read Objects ` page.
+
+.. literalinclude:: /examples/generated/react-native/ts/read.test.snippet.crud-read-filter-data.tsx
+ :language: typescript
+ :emphasize-lines: 6-8, 14-18
+
+*Parameters*
+
+- ``type: string`` A string that matches your object model's class name or a reference to a
+ class that extends :js-sdk:`Realm.Object `.
+- ``query?: QueryCallback`` A query function that can filter and sort query results. Builds on
+ ``useCallback`` to memoize the query function.
+- ``deps?: DependencyList`` A list of query function dependencies that's used to memoize
+ the query function.
+
+*Returns*
+
+- ``Realm.Results`` A Realm Object or ``null`` if no object is found.
+
+UserProvider Hooks
+~~~~~~~~~~~~~~~~~~
+
+.. _sdks-react-use-user-hook:
+
+useUser()
+`````````
+
+.. code:: typescript
+ :copyable: false
+ :caption: Type signature
+
+ useUser(): Realm.User
+
+The ``useUser()`` Hook provides access to the :js-sdk:`logged-in user `. For example,
+you can use ``useUser()`` to log the current user out.
+
+When changes to the user object happen, this Hook will rerender its parent
+component. For example, if you call ``user.refreshCustomData`` to get updated
+custom user data, the ``useUser()`` parent component will rerender.
+
+.. include:: /examples/generated/react-native/v12/RealmWrapper.snippet.log-user-out.tsx.rst
+
+*Returns*
+
+- ``Realm.User`` An instance of the currently-authenticated Atlas user.
+
+AppProvider Hooks
+~~~~~~~~~~~~~~~~~
+
+.. _sdks-react-use-app-hook:
+
+useApp()
+````````
+
+.. code:: typescript
+ :copyable: false
+ :caption: Type signature
+
+ useApp(): Realm.App
+
+*Example*
+
+The ``useApp()`` Hook provides access to a :js-sdk:`Realm.App `
+instance.
+
+.. include:: /examples/generated/react-native/v12/use-app.snippet.import-use-app.tsx.rst
+.. include:: /examples/generated/react-native/v12/use-app.snippet.use-app.tsx.rst
+
+*Returns*
+
+- ``Realm.App`` An instance of the current ``Realm.App`` created by ``AppProvider``.
+
+.. _sdks-react-use-auth-hook:
+
+useAuth()
+`````````
+
+.. code:: typescript
+ :copyable: false
+ :caption: Type signature
+
+ useAuth(): UseAuth
+
+You destructure the ``useAuth()`` Hook result into a ``result`` and authentication method.
+
+result
+++++++
+
+.. code:: typescript
+ :copyable: false
+ :caption: Type signature
+
+ result: AuthResult
+
+Result of authentication Hook operation. For example, ``result.operation`` gives
+you the name of the current operation.
+
+*Enum values*
+
+- ``state``: Can be ``"not-started"``, ``"pending"``, ``"success"``, ``"error"``.
+- ``operation``: For a list of all operation names, refer to the
+ :realm-react-sdk:`API documentation `.
+- ``pending``: Can be ``true`` or ``false``.
+- ``success``: Can be ``true`` or ``false``.
+- ``error``: Error-based object or undefined.
+
+Authentication Methods
+++++++++++++++++++++++
+
+The authentication method specifies how you want users to login to your app. ``useAuth`` has an authentication method for every App Services authentication provider.
+
+.. list-table::
+ :header-rows: 1
+ :stub-columns: 1
+ :widths: 23 20 57
+
+ * - Operation
+ - Parameter
+ - Example
+
+ * - logIn
+ - ``credentials``: An Atlas credential supplied by any supported Atlas App Services
+ authentication provider.
+ - Logs in a user with any authentication mechanism supported by Atlas App Services. If called when a
+ user is logged in, the current user switches to the new user. Usually, it's better to use
+ the more specific login methods.
+
+ .. code:: typescript
+
+ const {logIn, result} = useAuth();
+
+ useEffect(() => logIn(Realm.Credentials.anonymous()), []);
+
+ if(result.pending) {
+ return ()
+ }
+
+ if(result.error) {
+ return ()
+ }
+
+ if(result.success) {
+ return ()
+ }
+ //...
+
+ * - logInWithAnonymous
+ - None
+ - Log in with the anonymous authentication provider.
+
+ .. code:: typescript
+
+ const {logInWithAnonymous, result} = useAuth();
+ const performLogin = () => {
+ logInWithAnonymous();
+ };
+
+ * - logInWithApiKey
+ - ``key``: A string that is linked to an App Services user.
+ - Log in with an API key.
+
+ .. code:: typescript
+
+ const {logInWithApiKey, result} = useAuth();
+ const performLogin = () => {
+ const key = getApiKey();
+ // user defined function
+ logInWithApiKey(key);
+ };
+
+ * - logInWithEmailPassword
+ - ``credentials``: An object with ``email`` and ``password`` fields.
+ - Log in with Email/Password.
+
+ .. code:: typescript
+
+ const {logInWithEmailPassword, result} = useAuth();
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+
+ const performLogin = () => {
+ logInWithEmailPassword({email, password});
+ };
+
+ * - logInWithJWT
+ - ``credentials``: A string representation of a user's JWT.
+ - Log in with a JSON Web Token (JWT).
+
+ .. code:: typescript
+
+ const {logInWithJWT, result} = useAuth();
+
+ const performLogin = () => {
+ const token = authorizeWithCustomerProvider();
+ // user defined function
+ logInWithJWT(token);
+ };
+
+ * - logInWithGoogle
+ - ``credentials``: An object with an ``idToken`` or ``authCode`` field that
+ should contain the string token you get from Google Identity Services.
+ - Log in with Google.
+
+ .. code:: typescript
+
+ const {logInWithGoogle, result} = useAuth();
+
+ const performLogin = () => {
+ const token = getGoogleToken();
+ // user defined function
+ logInWithGoogle({idToken: token});
+ };
+
+ * - logInWithApple
+ - ``idToken``: A string you get from the Apple SDK.
+ - Log in with Apple.
+
+ .. code:: typescript
+
+ const {logInWithApple, result} = useAuth();
+
+ const performLogin = () => {
+ const token = getAppleToken();
+ // user defined function
+ logInWithApple(token);
+ };
+
+ * - logInWithFacebook
+ - ``accessToken``: A string you get from the Facebook SDK.
+ - Log in with Facebook.
+
+ .. code:: typescript
+
+ const {logInWithFacebook, result} = useAuth();
+
+ const performLogin = () => {
+ const token = getFacebookToken();
+ // user defined function
+ logInWithFacebook(token);
+ };
+
+ * - logInWithFunction
+ - ``payload``: An object that contains user information you want to pass to
+ the App Services function that processes log in requests.
+ - Log in with a custom function.
+
+ .. code:: typescript
+
+ const {logInWithFunction, result} = useAuth();
+
+ const performLogin = () => {
+ const customPayload = getAuthParams();
+ // user defined arguments
+ logInWithFunction(customPayload);
+ };
+
+ * - logOut
+ - None
+ - Logs out the current user.
+
+ .. code:: typescript
+
+ const {logOut, result} = useEmailPasswordAuth();
+ const performLogout = () => {
+ logOut();
+ }
+
+.. _sdks-react-use-emailpassword-auth-hook:
+
+useEmailPasswordAuth()
+``````````````````````
+
+You destructure the ``useEmailPasswordAuth()`` Hook result into a ``result`` and authentication operation.
+
+result
+++++++
+
+.. code:: typescript
+ :copyable: false
+ :caption: Type signature
+
+ result: AuthResult
+
+Result of operation. For example, ``result.operation`` gives you the name of the current
+operation.
+
+*Enum values*
+
+- ``state``: Can be ``"not-started"``, ``"pending"``, ``"success"``, ``"error"``.
+- ``operation``: For a list of all operation names, refer to the
+ :realm-react-sdk:`API documentation `.
+- ``pending``: Can be ``true`` or ``false``.
+- ``success``: Can be ``true`` or ``false``.
+- ``error``: Error-based object or undefined.
+
+Authentication Operations
++++++++++++++++++++++++++
+
+``useEmailPasswordAuth`` has a number of operations to facilitate email and password user authentication.
+
+.. list-table::
+ :header-rows: 1
+ :stub-columns: 1
+ :widths: 27 18 55
+
+ * - Operation
+ - Parameter
+ - Example
+
+ * - logIn
+ - ``credentials``: An object that contains ``email`` and ``password`` properties.
+ - Logs a user in using an email and password. You could also call
+ ``logIn(Realm.Credentials.emailPassword(email, password))``. Returns a
+ ``Realm.User`` instance of the logged-in user.
+
+ .. code:: typescript
+
+ const {logIn, result} = useEmailPasswordAuth();
+
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+
+ const performLogin = () => {
+ logIn({email, password});
+ };
+
+ if(result.pending) {
+ return ()
+ }
+
+ if(result.error) {
+ return ()
+ }
+
+ if(result.success) {
+ return ()
+ }
+ //...
+
+ * - logOut
+ - None
+ - Logs out the current user.
+
+ .. code:: typescript
+
+ const {logOut, result} = useEmailPasswordAuth();
+ const performLogout = () => {
+ logOut();
+ }
+
+ * - register
+ - ``args``: An object that contains ``email`` and ``password`` properties.
+ - Registers a new user. Requires a user email and password.
+
+ .. code:: typescript
+
+ const {register, result} = useEmailPasswordAuth();
+
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+
+ const performRegister = () => {
+ register({email, password});
+ };
+
+ * - confirm
+ - ``args``: An object that contains ``token`` and ``tokenId`` properties.
+ - Confirms a user account. Requires a ``token`` and ``tokenId``.
+
+ .. code:: typescript
+
+ const {confirm, result} = useEmailPasswordAuth();
+
+ const performConfirmation = () => {
+ confirm({token, tokenId});
+ };
+
+ * - resendConfirmationEmail
+ - ``args``: An object that contains an ``email`` property.
+ - Resends a confirmation email.
+
+ .. code:: typescript
+
+ const {resendConfirmationEmail, result} = useEmailPasswordAuth();
+ const [email, setEmail] = useState('');
+
+ const performResendConfirmationEmail = () => {
+ resendConfirmationEmail({email});
+ };
+
+ * - retryCustomConfirmation
+ - ``args``: An object that contains an ``email`` property.
+ - Retries confirmation with a custom function.
+
+ .. code:: typescript
+
+ const {retryCustomConfirmation, result} = useEmailPasswordAuth();
+ const [email, setEmail] = useState('');
+
+ const performRetryCustomConfirmation = () => {
+ retryCustomConfirmation({email});
+ };
+
+ * - sendResetPasswordEmail
+ - ``args``: An object that contains an ``email`` property.
+ - Sends a password reset email.
+
+ .. code:: typescript
+
+ const {sendResetPasswordEmail, result} = useEmailPasswordAuth();
+ const [email, setEmail] = useState('');
+
+ const performSendResetPasswordEmail = () => {
+ sendResetPasswordEmail({email});
+ };
+
+ * - resetPassword
+ - ``args``: An object that contains ``token``, ``tokenId``, and ``password``
+ properties.
+ - Resets a user's password.
+
+ .. code:: typescript
+
+ const {resetPassword, result} = useEmailPasswordAuth();
+ const [password, setPassword] = useState('');
+
+ const performResetPassword = () => {
+ resetPassword({token, tokenId, password});
+ };
+
+ * - callResetPasswordFunction
+ - ``args``: An object that contains ``email`` and ``password`` properties.
+
+ ``restArgs``: Additional arguments that you need to pass to your custom
+ reset password function.
+ - Calls your App Services backend password reset function. Can pass arguments to
+ the function as needed.
+
+ .. code:: typescript
+
+ const {callResetPasswordFunction, result} = useEmailPasswordAuth();
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+
+ const performResetPassword = () => {
+ callResetPasswordFunction({email, password}, "extraArg1", "extraArg2");
+ };
+
+.. _create_context_with_createrealmcontext:
+
+Create Context with createRealmContext()
+----------------------------------------
+
+.. code:: typescript
+ :copyable: false
+ :caption: Type signature
+
+ createRealmContext(realmConfig?): RealmContext
+
+Use ``createRealmContext()`` to configure more than one database. The ``createRealmContext()``
+method creates a `React Context `__
+object for a database with a given :realm-react-sdk:`Configuration
+`. The ``Context`` object contains the following:
+
+- A Context Provider component that wraps around child components
+ and provides them with access to Hooks.
+- Various prebuilt Hooks that access the configured database.
+
+To work with more than one database, you need to destructure a new provider and its
+associated Hooks from the result of ``createRealmContext()``. You can call this new database
+provider and use its associated Hooks the same way you would with the ``RealmProvider``
+imported from the ``@realm/react`` library. Namespace your providers to avoid confusion about
+which provider and Hooks you're working with.
+
+*Parameters*
+
+- ``realmConfig?: Realm.Configuration`` All properties of :realm-react-sdk:`BaseConfiguration
+ ` can be used.
+
+*Returns*
+
+- ``RealmContext`` An object containing a ``RealmProvider`` component, and the ``useRealm``,
+ ``useQuery`` and ``useObject`` Hooks.
+
+For a detailed guide, refer to :ref:`Expose More Than One Database `.
\ No newline at end of file
diff --git a/source/frameworks/react/quick-start.txt b/source/frameworks/react/quick-start.txt
new file mode 100644
index 0000000000..6eb5c218ac
--- /dev/null
+++ b/source/frameworks/react/quick-start.txt
@@ -0,0 +1,14 @@
+.. _sdks-react-quick-start:
+
+============================
+Quick Start for @realm/react
+============================
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Placeholder page for information about building with @realm/react. (This may
+be a directory as we have two pages for this?)
\ No newline at end of file
diff --git a/source/frameworks/react/react-to-changes.txt b/source/frameworks/react/react-to-changes.txt
new file mode 100644
index 0000000000..cf7f39b459
--- /dev/null
+++ b/source/frameworks/react/react-to-changes.txt
@@ -0,0 +1,14 @@
+.. _sdks-react-react-to-changes:
+
+================
+React to Changes
+================
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Placeholder page for information about building with @realm/react. (This may
+be a directory as we have two pages for this?)
\ No newline at end of file
diff --git a/source/frameworks/react/read-write.txt b/source/frameworks/react/read-write.txt
new file mode 100644
index 0000000000..36acafd058
--- /dev/null
+++ b/source/frameworks/react/read-write.txt
@@ -0,0 +1,14 @@
+.. _sdks-react-read-write:
+
+=================
+Read & Write Data
+=================
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Placeholder page for information about building with @realm/react. (This may
+be a directory as we have two pages for this?)
\ No newline at end of file
diff --git a/source/frameworks/react/sync-data.txt b/source/frameworks/react/sync-data.txt
new file mode 100644
index 0000000000..71c89a76cc
--- /dev/null
+++ b/source/frameworks/react/sync-data.txt
@@ -0,0 +1,14 @@
+.. _sdks-react-sync-data:
+
+=========
+Sync Data
+=========
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Placeholder page for information about building with @realm/react. (This may
+be a directory as we have two pages for this?)
\ No newline at end of file
diff --git a/source/frameworks/react/test-debug.txt b/source/frameworks/react/test-debug.txt
new file mode 100644
index 0000000000..225137ecee
--- /dev/null
+++ b/source/frameworks/react/test-debug.txt
@@ -0,0 +1,14 @@
+.. _sdks-react-test-debug:
+
+============
+Test & Debug
+============
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Placeholder page for information about building with @realm/react. (This may
+be a directory as we have two pages for this?)
\ No newline at end of file
diff --git a/source/frameworks/swiftui.txt b/source/frameworks/swiftui.txt
new file mode 100644
index 0000000000..8a6c77d0ee
--- /dev/null
+++ b/source/frameworks/swiftui.txt
@@ -0,0 +1,19 @@
+.. _sdks-build-with-swiftui:
+
+==================
+Build with SwiftUI
+==================
+
+.. toctree::
+ :titlesonly:
+
+ Quick Start
+ Model Data
+ Configure and Open a Database
+ React to Changes
+ Pass Data Between Views
+ Write Data
+ Filter Data
+ Handle Sync Errors
+ Sync Data in the Background
+ Use the SDK with SwiftUI Previews
diff --git a/source/frameworks/swiftui/background-sync.txt b/source/frameworks/swiftui/background-sync.txt
new file mode 100644
index 0000000000..09e1f13647
--- /dev/null
+++ b/source/frameworks/swiftui/background-sync.txt
@@ -0,0 +1,311 @@
+.. _swiftui-background-sync:
+
+=====================================
+Sync Data in the Background - SwiftUI
+=====================================
+
+.. meta::
+ :description: Learn how to use a SwiftUI BackgroundTask to sync data in the background.
+ :keywords: Realm, Swift SDK, code example
+
+.. facet::
+ :name: genre
+ :values: tutorial
+
+.. facet::
+ :name: programming_language
+ :values: swift
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Overview
+--------
+
+You can use a SwiftUI :apple:`BackgroundTask `
+to update a synced database when your app is in the background. This example
+demonstrates how to configure and perform background syncing in an iOS app.
+
+You can follow along with the example on this page using the SwiftUI Device
+Sync Template App. To get your own copy of the SwiftUI Device Sync
+Template App, check out the :ref:`Device Sync SwiftUI tutorial
+` and go through the :guilabel:`Prerequisites`
+and :guilabel:`Start with the Template` sections.
+
+Enable Background Modes for Your App
+------------------------------------
+
+To enable background tasks for your app:
+
+.. procedure::
+
+ .. step:: Add Background Modes Capability
+
+ Select your app Target, go to the :guilabel:`Signing & Capabilities`
+ tab, and click :guilabel:`+ Capability` to add the capability.
+
+ .. figure:: /images/xcode-select-target-add-capability.png
+ :alt: Screenshot of Xcode with app Target selected, Signing & Capabilities tab open, and arrow pointing to add Capabilities.
+ :lightbox:
+
+ Search for "background", and select :guilabel:`Background Modes`.
+
+ .. step:: Select Background Modes
+
+ Now you should see a :guilabel:`Background Modes` section in your
+ :guilabel:`Signing & Capabilities` tab. Expand this section, and
+ click the checkboxes to enable :guilabel:`Background fetch` and
+ :guilabel:`Background processing`.
+
+ .. step:: Update the Info.plist
+
+ Go to your project's :file:`Info.plist`, and add a new row for
+ ``Permitted background task scheduler identifiers``. If you are
+ viewing raw keys and values, the key is
+ ``BGTaskSchedulerPermittedIdentifiers``. This field is an array.
+ Add a new item to it for your background task identifier. Set the
+ new item's value to the string you intend to use as the identifier
+ for your background task. For example: ``refreshTodoRealm``.
+
+Schedule a Background Task
+--------------------------
+
+After enabling background processes for your app, you can start adding the
+code to the app to schedule and execute a background task. First, import
+``BackgroundTasks`` in the files where you will write this code:
+
+.. code-block:: swift
+ :emphasize-lines: 3
+
+ import SwiftUI
+ import RealmSwift
+ import BackgroundTasks
+
+Now you can add a scheduled background task. If you're following along
+via the Template App, you can update your ``@main`` view:
+
+.. code-block:: swift
+ :emphasize-lines: 3, 9-14
+
+ @main
+ struct realmSwiftUIApp: SwiftUI.App {
+ @Environment(\.scenePhase) private var phase
+
+ var body: some Scene {
+ WindowGroup {
+ ContentView(app: realmApp)
+ }
+ .onChange(of: phase) { newPhase in
+ switch newPhase {
+ case .background: scheduleAppRefresh()
+ default: break
+ }
+ }
+ }
+
+You can add an environment variable to store a change to the ``scenePhase``:
+``@Environment(\.scenePhase) private var phase``.
+
+Then, you can add the ``.onChange(of: phase)`` block that calls the
+``scheduleAppRefresh()`` function when the app goes into the background.
+
+Create the ``scheduleAppRefresh()`` function:
+
+.. code-block:: swift
+
+ func scheduleAppRefresh() {
+ let backgroundTask = BGAppRefreshTaskRequest(identifier: "refreshTodoRealm")
+ backgroundTask.earliestBeginDate = .now.addingTimeInterval(10)
+ try? BGTaskScheduler.shared.submit(backgroundTask)
+ }
+
+This schedules the work to execute the background task whose identifier you
+added to the Info.plist above when you enabled Background Modes. In this
+example, the identifier ``refreshTodoRealm`` refers to this task.
+
+Create the Background Task
+--------------------------
+
+Now that you've scheduled the background task, you need to create the background
+task that will run to update the synced realm.
+
+If you're following along with the Template App, you can add this
+``backgroundTask`` to your ``@main`` view, after the ``.onChange(of: phase)``:
+
+.. code-block:: swift
+ :emphasize-lines: 7-23
+
+ .onChange(of: phase) { newPhase in
+ switch newPhase {
+ case .background: scheduleAppRefresh()
+ default: break
+ }
+ }
+ .backgroundTask(.appRefresh("refreshTodoRealm")) {
+ guard let user = realmApp.currentUser else {
+ return
+ }
+ let config = user.flexibleSyncConfiguration(initialSubscriptions: { subs in
+ if let foundSubscription = subs.first(named: "user_tasks") {
+ foundSubscription.updateQuery(toType: Item.self, where: {
+ $0.owner_id == user.id
+ })
+ } else {
+ subs.append(QuerySubscription
- (name: "user_tasks") {
+ $0.owner_id == user.id
+ })
+ }
+ }, rerunOnOpen: true)
+ await refreshSyncedRealm(config: config)
+ }
+
+This background task first checks that your app has a logged-in user. If so,
+it sets a :swift-sdk:`.flexibleSyncConfiguration
+`
+with a :ref:`subscription ` the
+app can use to sync the realm.
+
+This is the same configuration used in the Template App's ``ContentView``.
+However, to use it here you need access to it farther up the view hierarchy.
+You could refactor this to a function you can call from either view that
+takes a :swift-sdk:`User ` as a
+parameter and returns a :swift-sdk:`Realm.configuration
+`.
+
+Finally, this task awaits the result of a function that actually syncs the
+database. Add this function:
+
+.. code-block:: swift
+
+ func refreshSyncedRealm(config: Realm.Configuration) async {
+ do {
+ try await Realm(configuration: config, downloadBeforeOpen: .always)
+ } catch {
+ print("Error opening the Synced realm: \(error.localizedDescription)")
+ }
+ }
+
+By opening this synced database and using the ``downloadBeforeOpen`` parameter
+to specify that you want to download updates, you load the fresh data into
+the database in the background. Then, when your app opens again, it already
+has the updated data on the device.
+
+.. important::
+
+ Do not try to write to the database directly in this background task. You
+ may encounter threading-related issues due to the SDK's thread-confined
+ architecture.
+
+Test Your Background Task
+-------------------------
+
+When you schedule a background task, you are setting the earliest time that
+the system could execute the task. However, the operating system factors in
+many other considerations that may delay the execution of the background task
+long after your scheduled ``earliestBeginDate``. Instead of waiting for a
+device to run the background task to verify it does what you intend, you can
+set a breakpoint and use LLDB to invoke the task.
+
+.. procedure::
+
+ .. step:: Configure a Device to Run Your App
+
+ To test that your background task is updating the synced database in the
+ background, you'll need a physical device running at minimum iOS 16.
+ Your device must be configured to run in :apple:`Developer Mode
+ `. If you get an
+ ``Untrusted Developer`` notification, go to :guilabel:`Settings`,
+ :guilabel:`General`, and :guilabel:`VPN & Device Management`. Here, you
+ can verify that you want to run the app you're developing.
+
+ Once you can successfully run your app on your device, you can test the
+ background task.
+
+ .. step:: Set a Breakpoint
+
+ Start by setting a breakpoint in your ``scheduleAppRefresh()`` function.
+ Set the breakpoint *after* the line where you submit the task to
+ ``BGTaskScheduler``. For this example, you might add a ``print`` line and
+ set the breakpoint at the print line:
+
+ .. code-block:: swift
+ :emphasize-lines: 5
+
+ func scheduleAppRefresh() {
+ let backgroundTask = BGAppRefreshTaskRequest(identifier: "refreshTodoRealm")
+ backgroundTask.earliestBeginDate = .now.addingTimeInterval(10)
+ try? BGTaskScheduler.shared.submit(backgroundTask)
+ print("Successfully scheduled a background task") // Set a breakpoint here
+ }
+
+ .. step:: Run the App
+
+ Now, run the app on the connected device. Create or sign into an account
+ in the app. If you're using the SwiftUI Template App, create some Items.
+ You should see the Items sync to the ``Item`` collection linked to your
+ Atlas App Services app.
+
+ Then, while leaving the app running in Xcode, send the app to the background
+ on your device. You should see the console print "Successfully scheduled a
+ background task" and then get an LLDB prompt.
+
+ .. step:: Add or Change Data in Atlas
+
+ While the app is in the background but still running in Xcode, Insert a new
+ document in the relevant Atlas collection that should sync to the device.
+ Alternately, change a value of an existing document that you created from
+ the device. After successfully running the background task, you should
+ see this data synced to the device from the background process.
+
+ If you're using the SwiftUI Template App, you can find relevant documents
+ in your Atlas cluster's ``Item`` collection. For more information on how
+ to add or change documents in Atlas, see: :atlas:`MongoDB Atlas: Create,
+ View, Update, and Delete Documents `.
+
+ .. step:: Invoke the Background Task in LLDB
+
+ Use this command to manually execute the background task in LLDB:
+
+ .. code-block:: shell
+
+ e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"refreshTodoRealm"]
+
+ If you have used a different identifier for your background task, replace
+ ``refreshTodoRealm`` with your task's identifier. This causes the task to
+ immediately begin executing.
+
+ If successful, you should see something like:
+
+ .. code-block:: shell
+
+ 2022-11-11 15:09:10.403242-0500 App[1268:196548] Simulating launch for task with identifier refreshTodoRealm
+ 2022-11-11 15:09:16.530201-0500 App[1268:196811] Starting simulated task
+
+ After you have kicked off the task, use the :guilabel:`Continue program execution`
+ button in the Xcode debug panel to resume running the app.
+
+ .. step:: Turn on Airplane Mode on the Device
+
+ After waiting for the background task to complete, but before you open the
+ app again, turn on Airplane Mode on the device. Make sure you have turned
+ off WiFi. This ensures that when you open the app again, it doesn't
+ start a fresh Sync and you see only the values that are now in the database
+ on the device.
+
+ .. step:: Open the App
+
+ Open the app on the device. You should see the updated data that you changed
+ in Atlas.
+
+ To verify the updates came through the background task, confirm you have
+ successfully disabled the network.
+
+ Create a new task using the app. You should see the task in the app, but
+ it should not sync to Atlas. Alternately, you could create or change data
+ in Atlas, but should not see it reflected on the device.
+
+ This tells you that the network has successfully been disabled,
+ and the updated data that you see came through the background task.
diff --git a/source/frameworks/swiftui/configure-and-open-database.txt b/source/frameworks/swiftui/configure-and-open-database.txt
new file mode 100644
index 0000000000..8d0d61f0d7
--- /dev/null
+++ b/source/frameworks/swiftui/configure-and-open-database.txt
@@ -0,0 +1,165 @@
+.. _swiftui-open-database:
+
+=======================================
+Configure and Open a Database - SwiftUI
+=======================================
+
+.. meta::
+ :description: Use the SDK's built in SwiftUI property wrappers to configure and open a database.
+ :keywords: Realm, Swift SDK, code example
+
+.. facet::
+ :name: genre
+ :values: reference
+
+.. facet::
+ :name: programming_language
+ :values: swift
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+The Swift SDK provides property wrappers to open a database in a
+SwiftUI-friendly way.
+
+You can:
+
+- :ref:`Implicitly open a database `
+ with a ``defaultConfiguration`` or specify a different configuration.
+ This works for both non-synced and synced databases.
+- :ref:`Always download changes before opening a synced database
+ `, which times out when the user is offline.
+- :ref:`Open a synced database even when a user is offline
+ `. The database may lack the most recent data.
+
+.. _swiftui-open-database-with-configuration:
+
+Open a Database with a Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When you use :swift-sdk:`@ObservedRealmObject `
+or :swift-sdk:`@ObservedResults `, these
+property wrappers implicitly open a database and retrieve the specified
+objects or results.
+
+.. literalinclude:: /examples/generated/swiftui/PassObjectsToView.snippet.implicitly-open-realm.swift
+ :language: swift
+
+.. include:: /includes/note-observedresults-swiftui-view.rst
+
+When you do not specify a configuration, these property wrappers use the
+:swift-sdk:`defaultConfiguration `.
+You can :ref:`set the defaultConfiguration `
+globally, and property wrappers across the app can use that configuration
+when they implicitly open a database.
+
+You can provide alternative configurations that the property wrappers use
+to implicitly open the database. You might want to do this when using
+multiple configurations in your app, as in cases where you have both
+a :swift-sdk:`SyncConfiguration ` and
+a local :swift-sdk:`Configuration `.
+To do this, :ref:`create explicit configurations `.
+Then, :ref:`use environment injection to pass the respective configurations
+to the views that need them `.
+Passing a configuration to a view where property wrappers open a database
+uses the passed configuration instead of the ``defaultConfiguration``.
+
+.. _swiftui-open-synced-database:
+
+Open a Synced Database
+~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 10.12.0
+
+These SwiftUI property wrappers open synced databases and populate views.
+The main difference between these property wrappers is whether the user
+must be online:
+
+- To download updates from your Atlas App Services app before opening a database,
+ use the :ref:`@AsyncOpen ` property wrapper.
+ This requires the user to have a network connection.
+- To open a synced database regardless of whether the user has a network
+ connection, use the :ref:`@AutoOpen `
+ property wrapper. This property wrapper enables developers to design
+ offline-first capabilities into their apps.
+
+.. _swiftui-async-open-synced-database:
+
+Download Changes Before Opening a Synced Database
+`````````````````````````````````````````````````
+
+Use the :swift-sdk:`@AsyncOpen ` property wrapper
+for apps that require up-to-date information from the server, such as game
+apps with live leaderboards that the user can play on multiple devices. This
+ensures the user is never using the app with stale data.
+
+You can add subscription queries in ``.onAppear`` after opening the database.
+
+.. literalinclude:: /examples/generated/swiftui/OpenRealm.snippet.fs-property-wrapper-sans-config-comment.swift
+ :language: swift
+
+You can create a :swift-sdk:`flexibleSyncConfiguration()
+`
+with the ``initialSubscriptions`` parameter. You can use this parameter
+to :ref:`subscribe to Sync queries ` in the
+configuration. If this runs more than once - for example, if it's in a view
+that reloads regularly - check whether the subscription exists already
+before adding it. Adding the same subscription again throws an error.
+
+.. literalinclude:: /examples/generated/swiftui/Authenticate.snippet.flexible-sync-config.swift
+ :language: swift
+
+Then, pass the configuration to the view that contains the property
+wrappers as an environment object.
+
+.. literalinclude:: /examples/generated/swiftui/Authenticate.snippet.inject-flex-sync-config-as-environment-object.swift
+ :language: swift
+
+For a complete example, see the :ref:`SwiftUI Quick Start `.
+
+This SwiftUI property wrapper initiates ``Realm.asyncOpen()`` for the current
+user. The property wrapper publishes states, represented by the :swift-sdk:`AsyncOpenState
+enum `,
+which you can use to update the view.
+
+.. example::
+
+ This example illustrates one way you might use ``@AsyncOpen`` to
+ open a database in a view. First, check for a user, or log them in.
+ Then, attempt to open the database, switching on the ``AsyncOpenState``
+ to display an appropriate view. When the database opens successfully,
+ inject it as an environment value to populate the view.
+
+ .. literalinclude:: /examples/generated/swiftui/OpenRealm.snippet.open-realm-view-flex-sync.swift
+ :language: swift
+
+.. _swiftui-auto-open-synced-database:
+
+Open a Synced Database Offline
+``````````````````````````````
+
+Like ``@AsyncOpen``, :swift-sdk:`@AutoOpen ` attempts
+to download updates before opening the database. However, if a network
+connection is not available, this method instead opens a database with
+data on the device.
+
+Use this property wrapper for apps where it's not a problem for the user
+to work with potentially stale data, such as note-taking apps where users
+should be able to work with data on the device
+
+.. code-block:: swift
+
+ @AutoOpen(appId: "app_id") var autoOpen
+
+This SwiftUI property wrapper attempts to download updates before opening a
+database for the current user. If there is no internet connection, this property
+wrapper instead returns the most up-to-date version of the local database file
+for the given ``appId`` and Sync configuration.
+
+The property wrapper publishes states, represented by the :swift-sdk:`AsyncOpenState
+enum `,
+which you can use to update the view. For a full example, see the ``@AsyncOpen``
+code examples above.
diff --git a/source/frameworks/swiftui/filter-data.txt b/source/frameworks/swiftui/filter-data.txt
new file mode 100644
index 0000000000..a94d3894cc
--- /dev/null
+++ b/source/frameworks/swiftui/filter-data.txt
@@ -0,0 +1,95 @@
+=====================
+Filter Data - SwiftUI
+=====================
+
+.. meta::
+ :description: Extend Apple's .searchable implementation to SDK objects, filter or query ObservedResults, or section results.
+ :keywords: Realm, Swift SDK, code example
+
+.. facet::
+ :name: genre
+ :values: reference
+
+.. facet::
+ :name: programming_language
+ :values: swift
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Observe in SwiftUI Views
+------------------------
+
+The ``@ObservedResults`` property wrapper used in the examples on this page
+is intended for use in a SwiftUI View. If you want to observe results
+in a view model instead, :ref:`register a change listener
+`.
+
+Search an SDK Collection
+------------------------
+
+The Swift SDK allows you to extend :apple:`.searchable
+`.
+When you use :swift-sdk:`ObservedResults `
+to query a realm, you can specify collection and keypath in the result set
+to mark it as searchable.
+
+The collection is the bound collection represented by your ``ObservedResults``
+query. In this example, it is the ``dogs`` variable that represents the
+collection of all Dog objects in the database.
+
+The keypath is the object property that you want to search. In this
+example, we search the dogs collection by dog name. The SDK's
+``.searchable`` implementation only supports keypaths with ``String`` types.
+
+.. literalinclude:: /examples/generated/swiftui/FilterData.snippet.searchable.swift
+ :language: swift
+
+Filter or Query a Database with ObservedResults
+-----------------------------------------------
+
+The :swift-sdk:`@ObservedResults ` property wrapper
+opens a database and returns all objects of the specified type. However, you
+can filter or query ``@ObservedResults`` to use only a subset of the objects
+in your view.
+
+.. seealso::
+
+ For more information about the query syntax and types of queries that the
+ SDK supports, refer to: :ref:`Read ` and
+ :ref:`sdks-filter-data-swift`.
+
+Query with the Swift Query API
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To use ``@ObservedResults`` with the :ref:`Swift Query API
+`, pass a query in a closure as an argument to
+``where``:
+
+.. literalinclude:: /examples/generated/swiftui/FilterData.snippet.type-safe-query-filter.swift
+ :language: swift
+
+Filter with an NSPredicate
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To filter ``@ObservedResults`` using the :ref:`NSPredicate Query API
+`, pass an :apple:`NSPredicate
+` as an argument to ``filter``:
+
+.. literalinclude:: /examples/generated/swiftui/FilterData.snippet.nspredicate-filter.swift
+ :language: swift
+
+Section Results
+---------------
+
+The :swift-sdk:`@ObservedSectionedResults `
+property wrapper opens a database and returns all objects of the specified type,
+divided into sections by the specified key path. Similar to
+``@ObservedResults`` above, you can filter or query ``@ObservedSectionedResults``
+to use only a subset of the objects in your view:
+
+.. literalinclude:: /examples/generated/swiftui/FilterData.snippet.observed-filtered-sectioned-results.swift
+ :language: swift
diff --git a/source/frameworks/swiftui/handle-sync-errors.txt b/source/frameworks/swiftui/handle-sync-errors.txt
new file mode 100644
index 0000000000..e33a06b832
--- /dev/null
+++ b/source/frameworks/swiftui/handle-sync-errors.txt
@@ -0,0 +1,72 @@
+.. _swiftui-handle-sync-errors:
+
+============================
+Handle Sync Errors - SwiftUI
+============================
+
+.. meta::
+ :description: Learn how to handle Sync errors in your SwiftUI-based app.
+ :keywords: Realm, Swift SDK, code example
+
+.. facet::
+ :name: genre
+ :values: reference
+
+.. facet::
+ :name: programming_language
+ :values: swift
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+.. _swiftui-register-sync-error-handler:
+
+Handle Sync Errors
+------------------
+
+While developing an application that uses Device Sync, set an error handler.
+This error handler detects and responds to any failed sync-related API calls.
+
+.. seealso::
+
+ For a complete example app with a working Sync error handler implementation,
+ :ref:`create a template app ` and check out
+ :ref:`the SwiftUI client `. The error handler
+ implementation is in the :file:`App.swift` file.
+
+For a SwiftUI-friendly implementation of a Sync error handler, create
+an ``ObservableObject`` with an optional ``@Published`` variable to contain
+a potential error. This handler uses the :swift-sdk:`SyncManager
+` to listen for errors.
+The ``SyncManager`` reports errors of the type ``SyncError``, and it also
+reports other connection issues.
+
+For more information, refer to the underlying Objective-C :objc-sdk:`RLMSyncError
+`.
+
+.. literalinclude:: /examples/generated/swiftui/HandleSyncErrors.snippet.swiftui-error-handler.swift
+ :language: swift
+
+.. include:: /includes/sync-errors-in-app-services.rst
+
+Initialize the error handler as a ``@StateObject``. Inject it into the
+view hierarchy as an environment object. In this example, we display an
+``.alert`` to the user when a Sync error occurs.
+
+.. literalinclude:: /examples/generated/swiftui/HandleSyncErrors.snippet.swiftui-app-with-error-handler.swift
+ :language: swift
+
+Then, in the view where you are observing the SDK ``App``, you can use the
+error handler as an ``@EnvironmentObject`` to react to Sync errors. An
+error that occurs here pops up an alert for the user, using the ``.alert``
+set in the view above.
+
+.. literalinclude:: /examples/generated/swiftui/HandleSyncErrors.snippet.use-app-and-error-handler-in-next-view.swift
+ :language: swift
+
+.. TODO: Re-test and add the section in the following file:
+.. `includes/swiftui-handle-client-reset-error.rst` after realm-swift merges
+.. this PR: https://github.com/realm/realm-swift/pull/8109
diff --git a/source/frameworks/swiftui/model-data.txt b/source/frameworks/swiftui/model-data.txt
new file mode 100644
index 0000000000..a3c1e69a10
--- /dev/null
+++ b/source/frameworks/swiftui/model-data.txt
@@ -0,0 +1,12 @@
+====================
+Model Data - SwiftUI
+====================
+
+.. toctree::
+ :titlesonly:
+
+ Object Models
+ Change an Object Model
+
+- :doc:`Object Models `
+- :doc:`Change an Object Model `
diff --git a/source/frameworks/swiftui/model-data/change-an-object-model.txt b/source/frameworks/swiftui/model-data/change-an-object-model.txt
new file mode 100644
index 0000000000..980c539e7c
--- /dev/null
+++ b/source/frameworks/swiftui/model-data/change-an-object-model.txt
@@ -0,0 +1,145 @@
+.. _swiftui-realm-migrations:
+
+================================
+Change an Object Model - SwiftUI
+================================
+
+.. meta::
+ :description: Learn how to update your data model and perform migrations in an app that uses Atlas Device SDK SwiftUI property wrappers.
+ :keywords: Realm, Swift SDK, code example
+
+.. facet::
+ :name: genre
+ :values: reference
+
+.. facet::
+ :name: programming_language
+ :values: swift
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+When you update your object schema, you must increment the schema version
+and perform a migration. You might update your object schema between major
+version releases of your app.
+
+For information on how to actually perform the migration, see:
+:ref:`Change an Object Model `.
+
+This page focuses on how to use migrated data in SwiftUI Views.
+
+.. include:: /includes/note-modify-schema-properties-of-synced-realms.rst
+
+Use Migrated Data with SwiftUI
+------------------------------
+
+To perform a migration:
+
+- Update your schema and write a migration block, if required
+- Specify a :swift-sdk:`Realm.Configuration `
+ that uses this migration logic and/or updated schema version when you
+ initialize your database.
+
+From here, you have a few options to pass the configuration object. You can:
+
+- Set the configuration as the :ref:`default configuration
+ `. If you do not explicitly pass the
+ configuration via environment injection or as a parameter, property
+ wrappers use the default configuration.
+- Use environment injection to provide this configuration to the first view
+ in your hierarchy that uses the database
+- Explicitly provide the configuration to an SDK property wrapper that takes
+ a configuration object, such as ``@ObservedResults`` or ``@AsyncOpen``.
+
+.. example::
+
+ For example, you might want to add a property to an existing object. We
+ could add a ``favoriteTreat`` property to the ``Dog`` object in DoggoDB:
+
+ .. code-block:: swift
+ :copyable: false
+
+ @Persisted var favoriteTreat = ""
+
+ After you add your new property to the schema, you must increment the
+ schema version. Your ``Realm.Configuration`` might look like this:
+
+ .. literalinclude:: /examples/generated/swiftui/SyncOrLocalRealm.snippet.update-schema-version.swift
+ :language: swift
+ :copyable: false
+
+ Declare this configuration somewhere that is accessible to the first view
+ in the hierarchy that needs it. Declaring this above your ``@main`` app
+ entrypoint makes it available everywhere, but you could also put it in
+ the file where you first open a realm.
+
+Set a Default Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can set a default configuration in a SwiftUI app the same as any other
+SDK app. Set the default database configuration by assigning a new
+``Realm.Configuration`` instance to the
+:swift-sdk:`Realm.Configuration.defaultConfiguration
+`
+class property.
+
+.. literalinclude:: /examples/generated/code/start/OpenCloseRealm.snippet.open-local-realm.swift
+ :language: swift
+
+Pass the Configuration Object as an Environment Object
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once you have declared the configuration, you can inject it as an environment
+object to the first view in your hierarchy that opens a database. If you are
+using the ``@ObservedResults`` or ``@ObservedRealmObject`` property wrappers,
+these views implicitly open a database, so they also need access to this
+configuration.
+
+.. code-block:: swift
+ :copyable: false
+
+ .environment(\.realmConfiguration, config)
+
+If your app uses either a non-synced or a synced database, the first view in
+the hiearchy that opens a database varies depending on whether you're using
+the app with or without Sync.
+
+Without sync, you can pass the database configuration environment object
+directly to the ``LocalOnlyContentView``:
+
+.. literalinclude:: /examples/generated/swiftui/SyncOrLocalRealm.snippet.pass-config-environment-object.swift
+ :language: swift
+
+Which opens a database implicitly with:
+
+.. literalinclude:: /examples/generated/swiftui/PassObjectsToView.snippet.local-only-view.swift
+ :language: swift
+
+However, when your app uses Sync, you open the database explicitly using the
+``@AsyncOpen`` or ``@AutoOpen`` property wrapper:
+
+.. literalinclude:: /examples/generated/swiftui/OpenRealm.snippet.open-realm-view-flex-sync.swift
+ :language: swift
+
+So you must pass the environment object to the view that explicitly
+opens the database. In this case, the ``OpenFlexibleSyncRealmView``.
+
+The important thing to remember is to make sure to pass the
+``Realm.Configuration`` that encompasses your migration logic to any view
+hierarchy that implicitly or explicitly opens a database.
+
+Explicitly Pass the Updated Configuration to an SDK SwiftUI Property Wrapper
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can explicitly pass the configuration object to an SDK SwiftUI
+property wrapper that takes a configuration object, such as ``@ObservedResults``
+or ``@AutoOpen``. In this case, you might pass it directly to ``@ObservedResults``
+in our ``DogsView``.
+
+.. code-block:: swift
+
+ // Use a `config` that you've passed in from above.
+ @ObservedResults(Dog.self, configuration: config) var dogs
diff --git a/source/frameworks/swiftui/model-data/define-an-object-model.txt b/source/frameworks/swiftui/model-data/define-an-object-model.txt
new file mode 100644
index 0000000000..5fa9d32ce5
--- /dev/null
+++ b/source/frameworks/swiftui/model-data/define-an-object-model.txt
@@ -0,0 +1,133 @@
+.. _swiftui-object-models:
+
+=======================
+Object Models - SwiftUI
+=======================
+
+.. meta::
+ :description: Model SDK objects for SwiftUI, and bind them to your views.
+ :keywords: Realm, Swift SDK, code example
+
+.. facet::
+ :name: genre
+ :values: tutorial
+
+.. facet::
+ :name: programming_language
+ :values: swift
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Concepts: Object Models and Relationships
+-----------------------------------------
+
+Modeling data for SwiftUI builds on the same object model and relationship
+concepts in Atlas Device SDK. If you are unfamiliar with the SDK's data
+modeling concepts, refer to: :ref:`sdks-object-models`.
+
+Binding the Object Model to the UI
+----------------------------------
+
+The Model-View-ViewModel (MVVM) design pattern advocates creating a view
+model that abstracts the model from the View code. While you can certainly
+do that with the SDK, the Swift SDK provides tools that make it easy to
+work directly with your data in SwiftUI Views. These tools include things
+like:
+
+- Property wrappers that create bindings to underlying observable objects
+- A class to project and transform underlying model objects for use in
+ specific views
+
+Transforming Data for SwiftUI Views
+-----------------------------------
+
+The Swift SDK provides a special type of object, called a
+:swift-sdk:`Projection `, to transform
+and work with subsets of your data. Consider a projection similar to
+a view model. It lets you pass through or transform the original
+object's properties in different ways:
+
+- Passthrough: The projection's property has the same name and type as
+ the original object.
+- Rename: The projection's property has the same type as the original object,
+ but a different name.
+- Keypath resolution: Use this to access specific properties of the
+ projected Object.
+- Collection mapping: You can map some :ref:`collection types
+ ` to a collection of primitive values.
+- Exclusion: All properties of the original SDK object not defined in
+ the projection model. Any changes to those properties do not trigger a
+ change notification when observing the projection.
+
+When you use a Projection, you get all the benefits of the SDK's
+live objects:
+
+- The class-projected object live updates
+- You can observe it for changes
+- You can apply changes directly to the properties in write transactions
+
+.. _swiftui-model:
+
+Define a New Object
+-------------------
+
+You can define an SDK object by deriving from the
+:swift-sdk:`Object ` or
+:swift-sdk:`EmbeddedObject `
+class. The name of the class becomes the table name in the database,
+and properties of the class persist in the database. This makes it
+as easy to work with persisted objects as it is to work with
+regular Swift objects.
+
+The SwiftUI documentation uses a model for a fictional app,
+DoggoDB. This app is a company directory of employees who have dogs. It
+lets people share a few details about their dogs with other employees.
+
+The data model includes a Person object, with a :ref:`to-many
+relationship ` to that person's Dog objects.
+It also uses a special Realm Swift SDK data type, :swift-sdk:`PersistableEnum
+`, to store information
+about the person's business unit.
+
+.. literalinclude:: /examples/generated/swiftui/Model.snippet.objects.swift
+ :language: swift
+
+.. seealso::
+
+ For complete details about defining an SDK object model, see:
+
+ - :ref:`Object Models `
+ - :ref:`Relationships `
+ - :ref:`Property Types `
+
+.. _swiftui-projection:
+
+Define a Projection
+-------------------
+
+Our fictional DoggoDB app has a user Profile view. This view displays
+some details about the person, but we don't need all of the properties
+of the ``Person`` model. We can create a :swift-sdk:`Projection
+` with only the details we want. We can also modify
+the ``lastName`` property to use just the first initial of the last name.
+
+.. literalinclude:: /examples/generated/swiftui/Model.snippet.projection.swift
+ :language: swift
+
+We can use this projection in the Profile view instead of the original
+``Person`` object.
+
+Class projection works with SwiftUI property wrappers:
+
+- :swift-sdk:`ObservedRealmObject `
+- :swift-sdk:`ObservedResults `
+
+.. seealso::
+
+ For a complete example of using a class projection in a SwiftUI
+ application, see :github:`the Projections example app
+ `.
diff --git a/source/frameworks/swiftui/pass-data-between-views.txt b/source/frameworks/swiftui/pass-data-between-views.txt
new file mode 100644
index 0000000000..2610beb8d5
--- /dev/null
+++ b/source/frameworks/swiftui/pass-data-between-views.txt
@@ -0,0 +1,79 @@
+.. _swiftui-pass-data-between-views:
+
+=================================
+Pass Data Between Views - SwiftUI
+=================================
+
+.. meta::
+ :description: Pass SDK objects explicitly to child views, access data in child views with property wrappers, or pass environment values to access the database.
+ :keywords: Realm, Swift SDK, code example
+
+.. facet::
+ :name: genre
+ :values: reference
+
+.. facet::
+ :name: programming_language
+ :values: swift
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Atlas Device SDK provides several ways to pass SDK data between views:
+
+- Pass SDK objects to a view
+- Use environment injection to:
+
+ - Inject an opened database into a view
+ - Inject a database configuration into a view
+
+Pass SDK Objects to a View
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When you use the ``@ObservedRealmObject`` or ``@ObservedResults`` property
+wrapper, you implicitly open a database and retrieve the specified objects
+or results. You can then pass those objects to a view further down the
+hierarchy.
+
+.. literalinclude:: /examples/generated/swiftui/PassObjectsToView.snippet.implicitly-open-realm-and-pass-objects.swift
+ :language: swift
+
+.. _swiftui-pass-environment-values:
+
+Pass Environment Values
+~~~~~~~~~~~~~~~~~~~~~~~
+
+:apple:`Environment ` injection is a
+useful tool in SwiftUI development with the SDK.
+Atlas Device SDK property wrappers provide different ways for you to
+work with environment values when developing your SwiftUI application.
+
+.. _swiftui-inject-database-as-environment-value:
+
+Inject an Opened Database
+`````````````````````````
+
+You can inject a database that you opened in another SwiftUI view into
+a view as an environment value. The property wrapper uses this passed-in
+database to populate the view:
+
+.. code-block:: swift
+
+ ListView()
+ .environment(\.realm, realm)
+
+.. _swiftui-inject-database-configuration:
+
+Inject a Database Configuration
+```````````````````````````````
+
+You can use a database other than the default database by passing a different
+configuration in an environment object.
+
+.. code-block:: swift
+
+ LocalOnlyContentView()
+ .environment(\.realmConfiguration, Realm.Configuration( /* ... */ ))
diff --git a/source/frameworks/swiftui/quick-start.txt b/source/frameworks/swiftui/quick-start.txt
new file mode 100644
index 0000000000..d131f755db
--- /dev/null
+++ b/source/frameworks/swiftui/quick-start.txt
@@ -0,0 +1,420 @@
+.. _ios-swiftui-quick-start:
+
+=====================
+Quick Start - SwiftUI
+=====================
+
+.. meta::
+ :description: Use Atlas Device SDK for Swift with SwiftUI property wrappers.
+ :keywords: Realm, Swift SDK, code example
+
+.. facet::
+ :name: genre
+ :values: tutorial
+
+.. facet::
+ :name: programming_language
+ :values: swift
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+This quick start demonstrates how to use the Atlas Device SDK for Swift
+with SwiftUI with a small working app. The app contains all of the code to
+get you up and running with SwiftUI and the SDK quickly.
+
+If you'd prefer to learn from a working example app, check out the
+SwiftUI :ref:`template app ` or the :ref:`swift-swiftui-tutorial`.
+
+Prerequisites
+-------------
+
+- :ref:`Install the Swift SDK. `
+- Meet the minimum Xcode and iOS targets required by the Swift SDK version.
+- Create a new Xcode project using the SwiftUI "App" template.
+
+Example App Overview
+--------------------
+
+This page contains all of the code for a working SwiftUI and Atlas Device SDK
+app. The app starts on the ``ItemsView``, where you can edit a list of items:
+
+- Press the ``Add`` button on the bottom right of the screen to add
+ randomly-generated items.
+- Press the ``Edit`` button on the top right to modify the list order,
+ which the app persists in the realm.
+- You can also swipe to delete items.
+
+When you have items in the list, you can press one of the items to
+navigate to the ``ItemDetailsView``. This is where you can modify the
+item name or mark it as a favorite:
+
+- Press the text field in the center of the screen and type a new name.
+ When you press Return, the item name should update across the app.
+- You can also toggle its favorite status by pressing the heart toggle in the
+ top right.
+
+.. tip::
+
+ This guide optionally integrates with :ref:`Device Sync `. See
+ :ref:`swiftui-integrate-with-sync` below.
+
+Get Started
+~~~~~~~~~~~
+
+We assume you have created an Xcode project with the SwiftUI "App"
+template. Open the main Swift file and delete all of the code inside,
+including any ``@main`` ``App`` classes that Xcode generated for you. At
+the top of the file, import the Realm and SwiftUI frameworks:
+
+.. literalinclude:: /examples/generated/swiftui/local/SwiftUIFlexSyncExampleApp.snippet.imports.swift
+ :language: swift
+
+.. tip::
+
+ Just want to dive right in with the complete code? Jump to
+ :ref:`swiftui-complete-code` below.
+
+.. _swiftui_quickstart-define-models:
+
+Define Models
+~~~~~~~~~~~~~
+
+A common SDK data modeling use case is to have "things" and
+"containers of things". This app defines two related SDK object models: item
+and itemGroup.
+
+An item has two user-facing properties:
+
+- A randomly generated-name, which the user can edit.
+- An ``isFavorite`` boolean property, which shows whether the user "favorited"
+ the item.
+
+An itemGroup contains items. You can extend the itemGroup to have a name and an
+association with a specific user, but that's out of scope of this guide.
+
+Paste the following code into your main Swift file to define the models:
+
+Because Sync does not automatically include linked objects, we must add
+``ownerId`` to both objects. You can omit ``ownerId`` if you only want to use
+a non-synced database.
+
+.. literalinclude:: /examples/generated/swiftui/flex-sync/SwiftUIFlexSyncExampleApp.snippet.flexible-sync-models.swift
+ :language: swift
+
+Views and Observed Objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The entrypoint of the app is the ``ContentView`` class that derives from
+``SwiftUI.App``. For now, this always displays the
+``LocalOnlyContentView``. Later, this will show the ``SyncContentView``
+when Device Sync is enabled.
+
+.. literalinclude:: /examples/generated/swiftui/local/SwiftUIFlexSyncExampleApp.snippet.content-view.swift
+ :language: swift
+
+.. tip::
+
+ You can use a database other than the default database by passing
+ an environment object from higher in the View hierarchy:
+
+ .. code-block:: swift
+
+ LocalOnlyContentView()
+ .environment(\.realmConfiguration, Realm.Configuration( /* ... */ ))
+
+The LocalOnlyContentView has an :swift-sdk:`@ObservedResults
+` itemGroups. This implicitly uses the default
+database to load all itemGroups when the view appears.
+
+This app only expects there to ever be one itemGroup. If there is an itemGroup
+in the database, the LocalOnlyContentView renders an ``ItemsView`` for
+that itemGroup.
+
+If there is no itemGroup already in the database, then the
+LocalOnlyContentView displays a ProgressView while it adds one. Because
+the view observes the itemGroups thanks to the ``@ObservedResults`` property
+wrapper, the view immediately refreshes upon adding that first itemGroup and
+displays the ItemsView.
+
+.. literalinclude:: /examples/generated/swiftui/local/SwiftUIFlexSyncExampleApp.snippet.local-only-content-view.swift
+ :language: swift
+
+.. tip::
+
+ Starting in SDK version 10.12.0, you can use an optional key path parameter
+ with ``@ObservedResults`` to filter change notifications to only those
+ occurring on the provided key path or key paths. For example:
+
+ .. code-block::
+
+ @ObservedResults(MyObject.self, keyPaths: ["myList.property"])
+
+The ItemsView receives the itemGroup from the parent view and stores it in
+an :swift-sdk:`@ObservedRealmObject `
+property. This allows the ItemsView to "know" when the object has
+changed regardless of where that change happened.
+
+The ItemsView iterates over the itemGroup's items and passes each item to an
+``ItemRow`` for rendering as a list.
+
+To define what happens when a user deletes or moves a row, we pass the
+``remove`` and ``move`` methods of the SDK
+:swift-sdk:`List ` as the handlers of the respective
+remove and move events of the SwiftUI List. Thanks to the
+``@ObservedRealmObject`` property wrapper, we can use these methods
+without explicitly opening a write transaction. The property wrapper
+automatically opens a write transaction as needed.
+
+.. literalinclude:: /examples/generated/swiftui/local/SwiftUIFlexSyncExampleApp.snippet.items-view.swift
+ :language: swift
+
+Finally, the ``ItemRow`` and ``ItemDetailsView`` classes use the
+``@ObservedRealmObject`` property wrapper with the item passed in from
+above. These classes demonstrate a few more examples of how to use the
+property wrapper to display and update properties.
+
+.. literalinclude:: /examples/generated/swiftui/local/SwiftUIFlexSyncExampleApp.snippet.item-row-and-details.swift
+ :language: swift
+
+.. tip::
+
+ ``@ObservedRealmObject`` is a frozen object. If you want to :ref:`modify
+ the properties ` of an ``@ObservedRealmObject``
+ directly in a write transaction, you must ``.thaw()`` it first.
+
+At this point, you have everything you need to work with Atlas Device SDK and
+SwiftUI on a device. Test it out and see if everything is working as expected.
+Read on to learn how to integrate this app with Device Sync.
+
+.. _swiftui-integrate-with-sync:
+
+Integrate Atlas Device Sync
+---------------------------
+
+Now that we have a working app, we can optionally integrate with Device Sync.
+Sync allows you to you see the changes you make across devices. Before you can
+add sync to this app, make sure to:
+
+- :ref:`Create an App Services App `.
+- :ref:`Enable anonymous authentication `.
+- :ref:`Enable Device Sync `.
+
+ 1. Specify a cluster and database.
+ #. Turn on Development Mode.
+ #. Use ``ownerId`` as the queryable field.
+ #. Enable Sync.
+
+- :ref:`Define the rules ` that determine which
+ permissions users have when using Device Sync. For this example, we assign
+ a default role, which applies to any collection that does not have a
+ collection-specific role. In this example, a user can read and write data
+ where the ``user.id`` of the logged-in user matches the ``ownerId`` of the
+ object:
+
+ .. literalinclude:: /includes/swiftui-tutorial-default-role.json
+ :language: json
+
+Now, deploy your application updates.
+
+.. tip::
+
+ The Sync version of this app changes the app flow a bit. The first
+ screen becomes the ``LoginView``. When you press the :guilabel:`Log
+ in` button, the app navigates to the ItemsView, where you see the
+ synced list of items in a single itemGroup.
+
+At the top of the source file, initialize an SDK :swift-sdk:`App
+` with :ref:`your App ID `:
+
+.. literalinclude:: /examples/generated/swiftui/flex-sync/SwiftUIFlexSyncExampleApp.snippet.mongodb-realm.swift
+ :language: swift
+
+.. tip::
+
+ You can change the app reference to ``nil`` to switch back to
+ non-Device Sync mode.
+
+Let's update the main ContentView to show the ``SyncContentView`` if the
+app reference is not ``nil``:
+
+.. literalinclude:: /examples/generated/swiftui/flex-sync/SwiftUIFlexSyncExampleApp.snippet.content-view.swift
+ :language: swift
+
+We define the SyncContentView below.
+
+The SyncContentView observes the SDK app instance. The app instance is
+the interface to the App Services backend, which provides the user
+authentication required for Sync. By observing the app instance, the
+SyncContentView can react when a user logs in or out.
+
+This view has two possible states:
+
+- If the SDK app does not have a currently logged-in user, show the ``LoginView``.
+- If the app does have a logged-in user, show the ``OpenSyncedRealmView``.
+
+In this view, after confirming we have a user, we create a
+:swift-sdk:`flexibleSyncConfiguration()
+`
+that includes the ``initialSubscriptions`` parameter. We can use this
+parameter to :ref:`subscribe to queryable fields
+`. These initial subscriptions
+search for data that matches the queries, and syncs that data to the
+realm. If no data matches the queries, the realm opens with an initial
+empty state.
+
+Your client application can only write objects that match the
+subscription query to a realm opened with a ``flexibleSyncConfiguration``.
+Trying to write objects that don't match the query causes the app to
+perform a compensating write to undo the illegal write operation.
+
+.. literalinclude:: /examples/generated/swiftui/flex-sync/SwiftUIFlexSyncExampleApp.snippet.flex-sync-content-view.swift
+ :language: swift
+
+In our subscriptions, we're querying for ``ItemGroup`` and ``Item`` objects
+where the ``ownerId`` matches the logged-in user's ``user.id``.
+Together with the permissions we used when we enabled Device Sync
+above, this means that the user can only read and write their own
+data.
+
+Device Sync does not automatically provide access to linked objects.
+Because of this, we must add subscriptions for both the ``ItemGroup`` and
+``Item`` objects - we can't just query for one or the other and get
+the related objects.
+
+From here, we pass the flexibleSyncConfiguration to the
+OpenSyncedRealmView as a ``realmConfiguration`` using an environment
+object. This is the view responsible for opening a database and working
+with the data. The SDK uses this configuration to search for data
+that should sync to the database.
+
+.. literalinclude:: /examples/generated/swiftui/flex-sync/SwiftUIFlexSyncExampleApp.snippet.realm-config-environment-object.swift
+ :language: swift
+
+Once logged in, we open the database asynchronously with the
+:swift-sdk:`AsyncOpen ` property wrapper.
+
+Because we've injected a ``flexibleSyncConfiguration()`` into the
+view as an environment value, the property wrapper uses this
+configuration to initiate Sync and download any matching data before
+opening the database. If we had not provided a configuration, the property
+wrapper would create a default ``flexibleSyncConfiguration()`` for us,
+and we could :ref:`subscribe to queries `
+in ``.onAppear``.
+
+.. literalinclude:: /examples/generated/swiftui/flex-sync/SwiftUIFlexSyncExampleApp.snippet.flex-sync-property-wrapper.swift
+ :language: swift
+
+The OpenSyncedRealmView switches on the :swift-sdk:`AsyncOpenState
+enum `, which lets us show different views
+based on the state. In our example, we show a ``ProgressView`` while we're
+connecting to the App and the database is syncing. We then open the
+database, passing the ``itemGroup`` to the ``ItemsView``, or show an
+``ErrorView`` if we can't open the database.
+
+.. tip::
+
+ When opening a synced database, use the :swift-sdk:`AsyncOpen
+ ` property wrapper to always download synced changes
+ before opening the database, or the :swift-sdk:`AutoOpen
+ ` property wrapper to open a database while syncing
+ in the background. ``AsyncOpen`` requires the user to be online,
+ while ``AutoOpen`` opens a database even if the user is offline.
+
+This view has a few different states:
+
+- While connecting or waiting for login, show a ``ProgressView``.
+- While downloading changes to the database, show a ``ProgressView`` with a
+ progress indicator.
+- When the database opens, check for an itemGroup object. If one does not exist
+ yet, create one. Then, show the ItemsView for the itemGroup in the database.
+ Provide a ``LogoutButton`` that the ItemsView can display on the top left
+ of the navigation bar.
+- If there is an error loading the database, show an error view containing
+ the error.
+
+When you run the app and see the main UI, there are no items in the view.
+That's because we're using anonymous login, so this is the first time this
+specific user logs in.
+
+.. literalinclude:: /examples/generated/swiftui/flex-sync/SwiftUIFlexSyncExampleApp.snippet.open-realm-view-flex-sync.swift
+ :language: swift
+
+In our subscriptions, we're querying for ``ItemGroup`` and ``Item`` objects
+where the ``ownerId`` matches the logged-in user's ``user.id``.
+Together with the permissions we used when we created the Sync app above, this
+means that the user can only read and write their own data.
+
+Sync does not automatically provide access to linked objects.
+Because of this, we must add subscriptions for both the ``ItemGroup`` and
+``Item`` objects - we can't just query for one or the other and get
+the related objects.
+
+With this in mind, we must also update the view here where we are
+creating a ``ItemGroup`` object. We must set the ``ownerId`` as the ``user.id``
+of the logged-in user.
+
+.. literalinclude:: /examples/generated/swiftui/flex-sync/SwiftUIFlexSyncExampleApp.snippet.add-ownerid-to-group.swift
+ :language: swift
+
+And we must also update the ``ItemsView`` to add ``ownerId`` when we
+create ``Item`` objects:
+
+.. literalinclude:: /examples/generated/swiftui/flex-sync/SwiftUIFlexSyncExampleApp.snippet.add-ownerid-to-create-button-code.swift
+ :language: swift
+
+Authenticate Users with Atlas App Services
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The LoginView maintains some state in order to display an activity
+indicator or error. It uses a reference to the app instance passed
+in from above to log in when the :guilabel:`Log in anonymously` button
+is clicked.
+
+.. tip::
+
+ In the LoginView, you can implement :ref:`email/password
+ authentication ` or :ref:`another
+ authentication provider `. For simplicity,
+ this example uses Anonymous authentication.
+
+Once login is complete, the LoginView itself doesn't need to do anything
+more. Because the parent view is observing the app, it notices
+when the user authentication state has changed and shows
+something other than the LoginView.
+
+.. literalinclude:: /examples/generated/swiftui/flex-sync/SwiftUIFlexSyncExampleApp.snippet.login-view.swift
+ :language: swift
+
+The LogoutButton works just like the LoginView, but logs out instead of
+logging in:
+
+.. literalinclude:: /examples/generated/swiftui/flex-sync/SwiftUIFlexSyncExampleApp.snippet.logout-button.swift
+ :language: swift
+
+Once logged in, the app follows the same flow as the non-Sync version.
+
+.. _swiftui-complete-code:
+
+Complete Code
+-------------
+
+If you would like to copy and paste or examine the complete code with or
+without Device Sync, see below.
+
+.. tabs::
+
+ .. tab:: Without Sync
+ :tabid: local
+
+ .. literalinclude:: /examples/generated/swiftui/local/SwiftUIFlexSyncExampleApp.snippet.complete-swiftui-flex-sync-quickstart.swift
+ :language: swift
+
+ .. tab:: With Flexible Sync
+ :tabid: flex-sync
+
+ .. literalinclude:: /examples/generated/swiftui/flex-sync/SwiftUIFlexSyncExampleApp.snippet.complete-swiftui-flex-sync-quickstart.swift
+ :language: swift
diff --git a/source/frameworks/swiftui/react-to-changes.txt b/source/frameworks/swiftui/react-to-changes.txt
new file mode 100644
index 0000000000..d158f46c2e
--- /dev/null
+++ b/source/frameworks/swiftui/react-to-changes.txt
@@ -0,0 +1,116 @@
+==========================
+React to Changes - SwiftUI
+==========================
+
+.. meta::
+ :description: Use the SDK's built in SwiftUI property wrappers to invalidate and update views when the database data changes.
+ :keywords: Realm, Swift SDK, code example
+
+.. facet::
+ :name: genre
+ :values: reference
+
+.. facet::
+ :name: programming_language
+ :values: swift
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+.. _swiftui-update-ui-when-objects-change:
+
+Observe an Object
+-----------------
+
+The SDK provides the :swift-sdk:`@ObservedRealmObject
+` property wrapper that invalidates a view
+when an observed object changes. You can use this property wrapper to
+create a view that automatically updates itself when the observed object
+changes.
+
+.. literalinclude:: /examples/generated/swiftui/PassObjectsToView.snippet.dog-detail-view.swift
+ :language: swift
+
+.. _swiftui-update-ui-when-query-results-change:
+
+Observe Query Results
+---------------------
+
+The SDK provides the :swift-sdk:`@ObservedResults `
+property wrapper that lets you observe a collection of query results. You
+can perform a quick write to an ``ObservedResults`` collection, and the view
+automatically updates itself when the observed query changes. For example,
+you can remove a dog from an observed list of dogs using ``onDelete``.
+
+.. include:: /includes/note-observedresults-swiftui-view.rst
+
+.. literalinclude:: /examples/generated/swiftui/PassObjectsToView.snippet.implicitly-open-realm-and-pass-objects.swift
+ :language: swift
+
+.. seealso::
+
+ For more information about the query syntax and types of queries that the
+ SDK supports, refer to: :ref:`Read ` and
+ :ref:`sdks-filter-data-swift`.
+
+Sort Observed Results
+~~~~~~~~~~~~~~~~~~~~~
+
+The :swift-sdk:`@ObservedResults `
+property wrapper can take a :swift-sdk:`SortDescriptor
+` parameter to sort the query results.
+
+.. literalinclude:: /examples/generated/swiftui/FilterData.snippet.sort-descriptor.swift
+ :language: swift
+
+.. tip::
+
+ You cannot use a computed property as a ``SortDescriptor`` for ``@ObservedResults``.
+
+.. _swiftui-observe-sectioned-results:
+
+Observe Sectioned Results
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 10.29.0
+
+You can observe a results set that is divided into sections by a key
+generated from a property on the object. We've added a computed variable
+to the model that we don't persist; we just use this to section the results
+set.
+
+.. literalinclude:: /examples/generated/swiftui/Model.snippet.computed-var-sectioned-results.swift
+ :language: swift
+
+Then, we can use the :swift-sdk:`@ObservedSectionedResults
+` property wrapper to
+observe the results set divided into sections based on the computed variable
+key.
+
+.. literalinclude:: /examples/generated/swiftui/SectionedResults.snippet.observed-sectioned-results.swift
+ :language: swift
+
+You might use these observed sectioned results to populate a List view
+divided by sections:
+
+.. literalinclude:: /examples/generated/swiftui/SectionedResults.snippet.sectioned-dogs-list-view.swift
+ :language: swift
+
+.. _swiftui-react-to-login-state-changes:
+
+Observe App State
+-----------------
+
+If your app uses Atlas Device Sync, you can observe the :swift-sdk:`App
+` object to react to login state changes. This enables
+your app to perform operations while it has an ``app.currentUser``, or direct
+the user to log in if there is no ``app.currentUser``.
+
+Because the SDK caches user credentials on the device, your app can work
+offline while it has an ``app.currentUser``.
+
+.. literalinclude:: /examples/generated/swiftui/Authenticate.snippet.flexible-sync-content-view.swift
+ :language: swift
diff --git a/source/frameworks/swiftui/swiftui-previews.txt b/source/frameworks/swiftui/swiftui-previews.txt
new file mode 100644
index 0000000000..6d83418746
--- /dev/null
+++ b/source/frameworks/swiftui/swiftui-previews.txt
@@ -0,0 +1,323 @@
+.. _swiftui-previews:
+
+=================================
+Use the SDK with SwiftUI Previews
+=================================
+
+.. meta::
+ :description: Use and debug SwiftUI Previews while developing with the Atlas Device SDK for Swift.
+ :keywords: Realm, Swift SDK, code example
+
+.. facet::
+ :name: genre
+ :values: tutorial
+
+.. facet::
+ :name: programming_language
+ :values: swift
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 3
+ :class: singlecol
+
+Overview
+--------
+
+SwiftUI Previews are a useful tool during development. You can work with the SDK
+data in SwiftUI Previews in a few ways:
+
+- Initialize individual objects to use in detail views
+- Conditionally use an array of objects in place of ``@ObservedResults``
+- Create a database that contains data for the previews
+
+SwiftUI Preview debugging can be opaque, so we also have a few tips to debug
+issue with persisting Realms within SwiftUI Previews.
+
+.. _swift-use-objects-in-a-detail-view:
+
+Initialize an Object for a Detail View
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the simplest case, you can use SwiftUI Previews with one or more objects
+that use SDK properties you can set directly at initialization.
+You might want to do this when previewing a Detail view. Consider DoggoDB's
+``DogDetailView``:
+
+.. literalinclude:: /examples/generated/swiftui/PassObjectsToView.snippet.dog-detail-view.swift
+ :language: swift
+
+Create an extension for your model object. Where you put this extension depends
+on convention in your codebase. You may put it directly in the model file,
+have a dedicated directory for sample data, or use some other convention in
+your codebase.
+
+In this extension, initialize one or more SDK objects with ``static let``:
+
+.. literalinclude:: /examples/generated/swiftui/Model.snippet.preview-extend-model-class-with-objects.swift
+ :language: swift
+
+In this example, we :ref:`initialize objects with a value
+`. You can only initialize objects with
+a value when your model contains properties that you can directly initialize.
+If your model object contains properties that are only mutable within a
+write transaction, such as a :ref:`List property `,
+you must instead :ref:`create a database to use with your SwiftUI Previews
+`.
+
+After you have initialized an object as an extension of your model class,
+you can use it in your SwiftUI Preview. You can pass the object directly
+to the View in the Preview:
+
+.. literalinclude:: /examples/generated/swiftui/Previews.snippet.preview-detail-view.swift
+ :language: swift
+
+.. _conditionally-use-observedresults-in-a-list-view:
+
+Conditionally Use ObservedResults in a List View
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When you use :swift-sdk:`@ObservedResults `
+in a List view, this implicitly opens a database and queries it. For this to
+work in a Preview, you need :ref:`a database populated with data
+`. As an alternative, you can conditionally
+use a static array in Previews and only use the ``@ObservedResults`` variable
+when running the app.
+
+You could do this in multiple ways, but for the sake of making our
+code easier to read and understand, we'll create an ``EnvironmentValue``
+that can detect whether the app is running in a Preview:
+
+.. code-block:: swift
+
+ import Foundation
+ import SwiftUI
+
+ public extension EnvironmentValues {
+ var isPreview: Bool {
+ #if DEBUG
+ return ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
+ #else
+ return false
+ #endif
+ }
+ }
+
+Then, we can use this as an environment value in our view, and conditionally
+change which variable we use based on whether or not we are in a Preview.
+
+This example builds on the :ref:`Dog extension we defined above
+`. We'll create an ``dogArray`` as
+a ``static let`` in our Dog extension, and include the item objects we
+already created:
+
+.. code-block:: swift
+
+ static let dogArray = [dog1, dog2, dog3]
+
+Then, when we iterate through our List, use the static ``dogArray`` if
+running in a Preview, or use the ``@ObservedResults`` query if not in a Preview.
+
+.. code-block:: swift
+
+ struct DogsView: View {
+ @Environment(\.isPreview) var isPreview
+ @ObservedResults(Dog.self) var dogs
+ var previewDogs = Dog.dogArray
+
+ var body: some View {
+ NavigationView {
+ VStack {
+ List {
+ if isPreview {
+ ForEach(previewDogs) { dog in
+ DogRow(dog: dog)
+ }
+ } else {
+ ForEach(dogs) { dog in
+ DogRow(dog: dog)
+ }.onDelete(perform: $dogs.remove)
+ }
+ }
+ ... More View code
+
+This has the benefit of being lightweight and not persisting any data, but
+the downside of making the View code more verbose. If you prefer cleaner
+View code, you can create a database with data that you use in the Previews.
+
+.. _swift-create-a-db-with-data:
+
+Create a Database with Data for Previews
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In some cases, your only option to see database data in a SwiftUI Preview
+is to create a database that contains the data. You might do this when populating
+a property that can only be populated during a write transaction, rather
+than initialized directly with a value, such as a :ref:`List
+` or :ref:`MutableSet `.
+You might also want to do this if your view relies on more complex object
+hierarchies being passed in from other views.
+
+However, using a database directly does inject state into your SwiftUI Previews,
+which can come with drawbacks. Whether you're using the SDK or Core Data,
+stateful SwiftUI Previews can cause issues like:
+
+- Seeing unexpected or duplicated data due to re-running the database file
+ creation steps repeatedly
+- Needing to perform a migration within the SwiftUI Preview when you make model changes
+- Potential issues related to changing state within views
+- Unexplained crashes or performance issues related to issues that are not
+ surfaced in a visible way in SwiftUI Previews
+
+You can avoid or fix some of these issues with these tips:
+
+- :ref:`Use an in-memory database, when possible (demonstrated in the example above) `
+- :ref:`Manually delete all preview data from the command line to reset state `
+- :ref:`Check out diagnostic logs to try to troubleshoot SwiftUI Preview issues `
+
+You can create a static variable for your database in your model extension.
+This is where you do the work to populate your database. In our case, we
+create a ``Person`` and append some ``Dog`` objects to the ``dogs``
+List property. This example builds on the example above where we :ref:`initialized
+a few Dog objects in an Dog extension `.
+
+We'll create a ``Person`` extension, and create a single ``Person`` object
+in that extension. Then, we'll create a ``previewRealm`` by adding the
+``Person`` we just created, and appending the example ``Dog`` objects from
+the ``Dog`` extension.
+
+To avoid adding these objects more than once, we add a check to see if the
+Person already exists by querying for Person objects and checking that
+the count is 1. If the realm contains a Person, we can use it in our
+SwiftUI Preview. If not, we add the data.
+
+.. literalinclude:: /examples/generated/swiftui/Model.snippet.extend-model-class-with-realm.swift
+ :language: swift
+
+To use it in the SwiftUI Preview, our ProfileView code expects a Profile.
+This is a :ref:`projection of the Person object `. In our
+Preview, we can get the database, query it for the Profile, and pass it to the
+view:
+
+.. literalinclude:: /examples/generated/swiftui/Previews.snippet.preview-with-realm.swift
+ :language: swift
+
+If you don't have a View that is expecting a database object to be passed in,
+but instead uses ``@ObservedResults`` to query a database or otherwise work
+with an existing database, you can :ref:`inject the database into the view as
+an environment value `:
+
+.. code-block:: swift
+
+ struct SomeListView_Previews: PreviewProvider {
+ static var previews: some View {
+ SomeListView()
+ .environment(\.realm, Person.previewRealm)
+ }
+ }
+
+.. _swiftui-preview-use-in-memory-database:
+
+Use an In-Memory Database
+`````````````````````````
+
+When possible, use an :ref:`in-memory database `
+to get around some of the state-related issues that can come from using
+a database within a SwiftUI Preview.
+
+Use the :swift-sdk:`inMemoryIdentifier
+`
+configuration property when you initialize the database.
+
+.. code-block:: swift
+
+ static var previewRealm: Realm {
+ var realm: Realm
+ let identifier = "previewRealm"
+ let config = Realm.Configuration(inMemoryIdentifier: identifier)
+ do {
+ realm = try Realm(configuration: config)
+ // ... Add data to realm
+
+.. note::
+
+ Do not use the :swift-sdk:`deleteRealmIfMigrationNeeded
+ `
+ configuration property when you initialize a database for SwiftUI Previews.
+ Due to the way Apple has implemented SwiftUI Previews, using this property
+ to bypass migration issues causes SwiftUI Previews to crash.
+
+.. _swiftui-preview-delete-db-from-preview:
+
+Delete SwiftUI Previews
+```````````````````````
+
+If you run into other SwiftUI Preview issues related to state,
+such as a failure to load a database in a Preview due to migration being
+required, there are a few things you can do to remove cached Preview data.
+
+The Apple-recommended fix is to close Xcode and use the command line to
+delete all your existing SwiftUI Preview data.
+
+1. Close Xcode.
+2. From your command line, run:
+
+ .. code-block:: shell
+
+ xcrun simctl --set previews delete all
+
+It's possible that data may persist after running this command. This is
+likely due to Xcode retaining a reference due to something in the Preview
+and being unable to delete it. You can also try these steps to resolve issues:
+
+- Build for a different simulator
+- Restart the computer and re-run ``xcrun simctl --set previews delete all``
+- Delete stored Preview data directly. This data is stored in
+ ``~/Library/Developer/Xcode/UserData/Previews``.
+
+.. _swiftui-preview-diagnose-crashes:
+
+Get Detailed Information about SwiftUI Preview Crashes
+``````````````````````````````````````````````````````
+
+If you have an unexplained SwiftUI Preview crash when using the SDK, first try
+running the application on the simulator. The error messaging and logs available
+for the simulator make it easier to find and diagnose issues. If you can
+debug the issue in the simulator, this is the easiest route.
+
+If you cannot replicate a SwiftUI Preview crash in the simulator, you can
+view crash logs for the SwiftUI Preview app. These logs are available in
+``~/Library/Logs/DiagnosticReports/``. These logs sometimes appear after
+a delay, so wait a few minutes after a crash if you don't see the relevant
+log immediately.
+
+.. _swift-use-a-synced-database-in-previews:
+
+Use a Synced Database in Previews
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If your app uses Atlas Device Sync, you may wonder how to use a synced database
+in your SwiftUI Previews. A better practice is to use static objects or a
+local database that you populate with data for your SwiftUI Previews.
+
+In our example app, we can preview a view *associated* with Device Sync -
+the LoginView - without needing to use a database at all:
+
+.. literalinclude:: /examples/generated/swiftui/Previews.snippet.preview-view-associated-with-sync.swift
+ :language: swift
+
+Since we're only viewing the static UI, we don't need to worry about the
+``SyncContentView`` that contains the logic of whether to show the ``LoginView``
+or go to the ``OpenSyncedRealmView``. We can also skip previewing the
+``OpenSyncedRealmView``, because that just handles logic associated with opening
+a synced database and populating it for the ``DogsView``. So the next view we
+want to see in a Preview is the ``DogsView``.
+
+Fortunately, the code to work with the SDK doesn't care whether
+the database uses Device Sync or not - you work with the database in the same way.
+So we can use the same non-synced database that we :ref:`created in the example
+above ` in the SwiftUI Preview.
+
+.. literalinclude:: /examples/generated/swiftui/Previews.snippet.preview-dogs-view.swift
+ :language: swift
diff --git a/source/frameworks/swiftui/write.txt b/source/frameworks/swiftui/write.txt
new file mode 100644
index 0000000000..d73a08e067
--- /dev/null
+++ b/source/frameworks/swiftui/write.txt
@@ -0,0 +1,209 @@
+====================
+Write Data - SwiftUI
+====================
+
+.. meta::
+ :description: Perform a quick write directly from a SwiftUI view, or use Swift code to update objects in the database.
+ :keywords: Realm, Swift SDK, code example
+
+.. facet::
+ :name: genre
+ :values: tutorial
+
+.. facet::
+ :name: programming_language
+ :values: swift
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Perform a Quick Write
+---------------------
+
+In addition to performing writes inside a transaction block, the Swift
+SDK offers a convenience feature to enable quick writes without explicitly
+performing a write transaction.
+
+When you use the ``@ObservedRealmObject`` or ``@ObservedResults`` property
+wrappers, you can implicitly open a write transaction. Use the ``$`` operator
+to create a two-way binding to the state object. Then, when you make changes
+to the bound object or collection, you initiate an implicit write.
+
+The SwiftUI property wrappers work with :ref:`frozen data
+` to provide thread safety. When you use ``$``
+to create a two-way binding, the Swift SDK manages thawing the frozen objects
+so you can write to them.
+
+Update an Object's Properties
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In this example, we create a two-way binding with one of the state object's
+properties. ``$dog.favoriteToy`` creates a binding to the model Dog
+object's ``favoriteToy`` property
+
+When the app user updates that field in this example, the SDK
+opens an implicit write transaction and saves the new value to the database.
+
+.. literalinclude:: /examples/generated/swiftui/QuickWrite.snippet.quick-write-property.swift
+ :language: swift
+
+Add or Remove Objects in an ObservedResults Collection
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+While a regular :ref:`Results collection `
+is immutable, :swift-sdk:`ObservedResults `
+is a mutable collection that allows you to perform writes using a two-way
+binding. When you update the bound collection, the SDK opens an implicit write
+transaction and saves the changes to the collection.
+
+In this example, we remove an element from the results set using
+``$dogs.remove`` in the ``onDelete``. Using the ``$dogs`` here creates a
+two-way binding to a ``BoundCollection`` that lets us mutate the
+``@ObservedResults`` ``dogs`` collection.
+
+We add an item to the results using ``$dogs.append`` in the
+``addDogButton``.
+
+These actions write directly to the ``@ObservedResults`` collection.
+
+.. literalinclude:: /examples/generated/swiftui/QuickWrite.snippet.update-observed-results.swift
+ :language: swift
+ :emphasize-lines: 15, 27
+
+.. include:: /includes/note-observedresults-swiftui-view.rst
+
+Append an Object to a List
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When you have a two-way binding with an ``@ObservedRealmObject`` that has
+a list property, you can add new objects to the list.
+
+In this example, the ``Person`` object has a list property that forms a
+:ref:`to-many relationship ` with one or more dogs.
+
+.. code-block:: swift
+
+ class Person: Object, ObjectKeyIdentifiable {
+ @Persisted(primaryKey: true) var _id: ObjectId
+ @Persisted var firstName = ""
+ @Persisted var lastName = ""
+ ...
+ @Persisted var dogs: List
+ }
+
+When the user presses the ``Save`` button, this:
+
+- Creates a ``Dog`` object with the details that the user has entered
+- Appends the ``Dog`` object to the ``Person`` object's ``dogs`` list
+
+.. include:: /examples/generated/swiftui/CreateObjects.snippet.add-dog-to-person-view.swift.rst
+
+Use Create to Copy an Object Into the Database
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There may be times when you create a new object, and set one of its properties
+to an object that already exists in the database. Then, when you go to add the
+new object to the database, you see an error similar to:
+
+.. code-block:: shell
+
+ Object is already managed by another Realm. Use create instead to copy it into this Realm.
+
+When this occurs, you can use the :swift-sdk:`.create
+`
+method to initialize the object, and use ``modified: .update`` to set its
+property to the existing object.
+
+.. example::
+
+ Consider a version of the DoggoDB ``Dog`` model where the ``favoriteToy``
+ property isn't just a ``String``, but is an optional ``DogToy`` object:
+
+ .. code-block:: swift
+
+ class Dog: Object, ObjectKeyIdentifiable {
+ @Persisted(primaryKey: true) var _id: UUID
+ @Persisted var name = ""
+ ...
+ @Persisted var favoriteToy: DogToy?
+ ...
+ }
+
+ When your app goes to create a new ``Dog`` object, perhaps it checks to see
+ if the ``DogToy`` already exists in the database, and then set the
+ ``favoriteToy`` property to the existing dog toy.
+
+ When you go to append the new ``Dog`` to the ``Person`` object, you may
+ see an error similar to:
+
+ .. code-block:: shell
+
+ Object is already managed by another Realm. Use create instead to copy it into this Realm.
+
+ The ``Dog`` object remains unmanaged until you append it to the ``Person``
+ object's ``dogs`` property. When the Swift SDK checks the ``Dog`` object to
+ find the database that is currently managing it, it finds nothing.
+
+ When you use the ``$`` notation to perform a quick write that appends the
+ ``Dog`` object to the ``Person`` object, this write uses the database it has
+ access to in the view. This is a database instance implicitly opened by
+ the ``@ObservedRealmObject`` or ``@ObservedResults`` property wrapper.
+ The existing ``DogToy`` object, however, may be managed by a different
+ realm instance.
+
+ To solve this error, use the :swift-sdk:`.create
+ `
+ method when you initialize the ``Dog`` object, and use
+ ``modified: .update`` to set its ``favoriteToy`` value to the existing
+ object:
+
+ .. literalinclude:: /examples/generated/code/start/SwiftUI.snippet.copy-to-realm-with-create.swift
+ :language: swift
+
+Perform an Explicit Write
+-------------------------
+
+In some cases, you may want or need to explicitly perform a write transaction
+instead of using the implicit ``$`` to perform a quick write. You may want
+to do this when:
+
+- You need to look up additional objects to perform a write
+- You need to perform a write to objects you don't have access to in the view
+
+If you pass an object you are observing with ``@ObservedRealmObject`` or
+``@ObservedResults`` into a function where you perform an explicit write
+transaction that modifies the object, you must thaw it first.
+
+.. literalinclude:: /examples/generated/code/start/SwiftUI.snippet.thaw-the-passed-in-object.swift
+ :language: swift
+
+You can access the database that is managing the object or objects by calling
+``.realm`` on the object or collection:
+
+.. literalinclude:: /examples/generated/code/start/SwiftUI.snippet.get-an-object-realm.swift
+ :language: swift
+
+Because the SwiftUI property wrappers use frozen objects, you must thaw
+the database before you can write to it.
+
+.. example::
+
+ Consider a version of the DoggoDB app where a ``Company`` object
+ has a list of ``Employee`` objects. Each ``Employee`` has a list of
+ ``Dog`` objects. But for business reasons, you also wanted to have a
+ list of ``Dog`` objects available directly on the ``Company`` object,
+ without being associated with an ``Employee``. The model might look
+ something like:
+
+ .. literalinclude:: /examples/generated/code/start/SwiftUI.snippet.swiftui-company-model.swift
+ :language: swift
+
+ Consider a view where you have access to the ``Company`` object, but
+ want to perform an explicit write to add an existing dog to an existing
+ employee. Your function might look something like:
+
+ .. literalinclude:: /examples/generated/code/start/SwiftUI.snippet.write-with-swiftui-observed-realm-object.swift
+ :language: swift
diff --git a/source/help.txt b/source/help.txt
index 9f43fb0478..def4292f00 100644
--- a/source/help.txt
+++ b/source/help.txt
@@ -4,16 +4,33 @@
Get Help
========
+.. meta::
+ :description: Get help for Atlas Device SDK through community forums, sharing feedback, or professional support.
+ :keywords: Realm, C++ SDK, Flutter SDK, Kotlin SDK, Java SDK, .NET SDK, Node.js SDK, Swift SDK
+
+.. facet::
+ :name: genre
+ :values: reference
+
+.. facet::
+ :name: programming_language
+ :values: cpp, csharp, dart, java, javascript/typescript, kotlin, objective-c, swift
+
.. contents:: On this page
:local:
:backlinks: none
- :depth: 1
+ :depth: 2
:class: singlecol
-Overview
---------
+.. tabs-selector:: drivers
+
+MongoDB provides various resources for getting help with Atlas Device SDK and
+Atlas App Services.
+
+.. tip:: Atlas Device SDK and Realm
-MongoDB provides various resources for getting help with Atlas App Services.
+ While the SDK has been renamed to Atlas Device SDK, some resources still
+ reflect ``Realm`` naming.
Professional Support
--------------------
@@ -35,9 +52,9 @@ Community Forums
The official `MongoDB Community Forums
`__ are a great
place to meet other developers, ask and answer questions, and stay
-up-to-date with the latest Realm and App Services features and releases. You can also
-interact with MongoDB employees, like our community team, engineers, and
-product managers, who are active forum contributors.
+up-to-date with the latest Atlas Device SDK and App Services features and
+releases. You can also interact with MongoDB employees, like our community
+team, engineers, and product managers, who are active forum contributors.
Stack Overflow
--------------
@@ -68,62 +85,56 @@ at the bottom right or right side of the page.
Bug Reporting, and Changelogs
-----------------------------
-.. tabs-realm-sdks::
+You can report bugs or view the changelog in the relevant ``realm-SDK``
+repository in GitHub. While the SDK has been renamed to Atlas Device SDK, the
+GitHub repositories still reflect ``realm`` naming.
- .. tab::
- :tabid: android
-
- For the Java SDK:
-
- - :github:`Report a bug `
- - :github:`View the changelog `
+.. tabs-drivers::
.. tab::
- :tabid: ios
+ :tabid: cpp-sdk
- For the Swift SDK:
-
- - :github:`Report a bug `
- - :github:`View the changelog `
+ - :github:`Report a bug `
+ - :github:`View the changelog `
.. tab::
- :tabid: dotnet
-
- For the .NET SDK:
+ :tabid: csharp
- :github:`Report a bug `
- :github:`View the changelog `
+
+ .. tab::
+ :tabid: dart
+
+ - :github:`Report a bug `
+ - :github:`View the changelog `
.. tab::
:tabid: javascript
- For the Node.js, React Native, or Web SDKs:
-
- :github:`Report a bug `
- :github:`View the changelog `
- .. tab::
- :tabid: flutter
-
- For the Flutter SDK:
-
- - :github:`Report a bug `
- - :github:`View the changelog `
-
.. tab::
:tabid: kotlin
- For the Kotlin SDK:
-
- :github:`Report a bug `
- :github:`View the changelog `
.. tab::
- :tabid: cpp
+ :tabid: objectivec
- For the C++ SDK:
+ - :github:`Report a bug `
+ - :github:`View the changelog `
- - :github:`Report a bug `
- - :github:`View the changelog `
+ .. tab::
+ :tabid: swift
+
+ - :github:`Report a bug `
+ - :github:`View the changelog `
-
+ .. tab::
+ :tabid: typescript
+
+ - :github:`Report a bug `
+ - :github:`View the changelog `
diff --git a/source/hidden-pages/upgrade-to-v2.txt b/source/hidden-pages/upgrade-to-v2.txt
new file mode 100644
index 0000000000..f7a339147b
--- /dev/null
+++ b/source/hidden-pages/upgrade-to-v2.txt
@@ -0,0 +1,206 @@
+:orphan:
+
+.. _flutter-upgrade-v2:
+
+=============================
+Upgrade to Flutter SDK v2.0.0
+=============================
+
+.. meta::
+ :description: Upgrade your existing Flutter or Dart app to Flutter SDK version 2.0.0 or later.
+ :keywords: code example, migration, migrate
+
+.. facet::
+ :name: genre
+ :values: tutorial
+
+.. contents:: On this page
+ :local:
+ :backlinks: none
+ :depth: 2
+ :class: singlecol
+
+Atlas Device SDK for Flutter version 2.0.0 introduces several breaking changes
+that impact existing apps upgrading from an earlier version.
+
+Notably, this version of the SDK:
+
+- Changes the part builder and how the SDK generates files for
+ your data model classes. This change impacts all apps upgrading from an
+ earlier version of the SDK. Refer to the :ref:`flutter-v2-builder-breaking-changes`
+ section on this page for information and instructions.
+
+- Removes or replaces several classes and members. These changes may or may not impact your
+ app. Refer to the :ref:`flutter-v2-removed-classes` section
+ on this page for information and instructions for impacted apps.
+
+.. _flutter-v2-builder-breaking-changes:
+
+Builder Changes
+---------------
+
+.. important::
+
+ This change impacts all apps upgrading from an earlier version of the SDK.
+
+Flutter SDK version 2.0.0 updates the SDK's ``realm_generator`` to use a
+``PartBuilder`` instead of a ``SharedPartBuilder``.
+This updated builder generates ``RealmModel`` data model files with a new
+``.realm.dart`` file extension:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 25 25 50
+
+ * - Version
+ - File Extension
+ - Example Part Directive
+
+ * - SDK v2.0.0 and later
+ - ``.realm.dart``
+ - .. literalinclude:: /examples/generated/flutter/migrate_parts.snippet.part-directive-new.dart
+ :language: dart
+
+ * - SDK v1.9.0 and earlier
+ - ``.g.dart``
+ - .. literalinclude:: /examples/generated/flutter/migrate_parts.snippet.part-directive-old.dart
+ :language: dart
+
+.. tip::
+
+ The update from ``SharedPartBuilder`` to ``PartBuilder`` makes it easier
+ to use multiple builders in your app. For example, combining ``realm_dart``
+ with a serialization package builder such as ``dart_mappable`` or
+ ``json_serializable``.
+
+.. _flutter-v2-what-do-i-need-to-do:
+
+What Do I Need to Do?
+~~~~~~~~~~~~~~~~~~~~~
+
+When you upgrade an existing app from an earlier version of the Flutter SDK to
+version 2.0.0 or later, you *must* update any existing part declarations, then
+regenerate the object models with the new ``.realm.dart`` file extension:
+
+.. procedure::
+
+ .. step:: Update Your Existing Part Declarations
+
+ Update all of the ``RealmObject`` part declarations in your app to
+ use the new naming convention:
+
+ .. literalinclude:: /examples/generated/flutter/migrate_parts.snippet.migrate-model-dart-new.dart
+ :language: dart
+ :emphasize-lines: 3-5
+
+ .. step:: Regenerate Your Object Models
+
+ .. tabs::
+
+ .. tab:: Flutter
+ :tabid: flutter
+
+ After you update all of your declarations, regenerate your
+ object models with the new ``.realm.dart`` file extension.
+ You can safely delete any ``.g.dart`` files from your project.
+
+ .. code-block::
+
+ dart run realm generate
+
+ .. tab:: Dart Standalone
+ :tabid: dart
+
+ After you update all of your declarations, regenerate your
+ object models with the new ``.realm.dart`` file extension.
+ You can safely delete any ``.g.dart`` files from your project.
+
+ .. code-block::
+
+ dart run realm_dart generate
+
+.. _flutter-v2-removed-classes:
+
+Removed Classes and Members
+---------------------------
+
+Flutter SDK version 2.0.0 also removed or replaced several classes, members, and properties
+from the SDK. These changes may or may not impact your app.
+
+The following table outlines what was removed and why, as well as a recommended solution
+when upgrading an app that used the removed class or member, if any:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 33 33 33
+
+ * - Removed Class or Member
+ - Reason
+ - Solution
+
+ * - ``AppConfiguration.localAppName`` and ``AppConfiguration.localAppVersion``
+ - Unused in SDK.
+ - Remove any instances.
+
+ * - ``ClientResetError.isFatal``
+ - Not needed. Always ``true``.
+ - Remove any instances.
+
+ * - ``ClientResetError.sessionErrorCode``
+ - Consolidated into ``SyncErrorCode`` in SDK v1.6.0.
+ - Use ``SyncErrorCode`` enum. See also the
+ :flutter-sdk:`SyncError ` API reference.
+
+ * - ``Realm.logger.level``
+ - Replaced by ``Realm.logger.setLogLevel``.
+ - Replace any instances. See also :ref:`sdks-logging`.
+
+ * - ``RealmProperty.indexed``
+ - Replaced by ``RealmProperty.indexType``.
+ - Replace any instances.
+
+ * - ``RealmValue.type``
+ - Changed to an enum of ``RealmValueType``.
+ - Replace any instances. See also :ref:`RealmValue Data Type `.
+
+ * - ``RealmValue.uint8List``
+ - Renamed to ``RealmValue.binary``.
+ - Replace any instances. See also :ref:`RealmValue Data Type `.
+
+ * - ``SchemaObject.properties``
+ - ``SchemaObject`` changed to an iterable collection of ``SchemaProperty``.
+ - Replace any instances. See also the
+ :flutter-sdk:`SchemaObject ` API reference.
+
+ * - ``SyncError`` constructor and ``SyncError.create`` factory
+ - Sync errors should only be created internally by the SDK.
+ - Remove any instances.
+
+ * - ``SyncClientError``, ``SyncConnectionError``, ``SyncSessionError``,
+ ``SyncResolveError``, ``SyncWebSocketError``, and ``GeneralSyncError``
+ - Consolidated into ``SyncError`` in SDK v1.6.0.
+ - Use ``SyncError`` or its subclasses. See also the
+ :flutter-sdk:`SyncError ` API reference.
+
+ * - ``SyncErrorCategory``, ``SyncClientErrorCode``, ``SyncConnectionErrorCode``,
+ ``SyncSessionErrorCode``, ``SyncResolveErrorCode``,``SyncWebsocketErrorCode``,
+ and ``GeneralSyncErrorCode``
+ - Consolidated into ``SyncErrorCode`` in SDK v1.6.0.
+ - Use ``SyncErrorCode`` enum. See also the
+ :flutter-sdk:`SyncError ` API reference.
+
+ * - ``SyncError.codeValue``, ``SyncError.category``, and ``SyncError.detailedMessage``
+ - Consolidated into ``SyncError`` in SDK v1.6.0. Messages were unused.
+ - Remove any category or message instances. Replace ``SyncError.codeValue``
+ with ``SyncError.code.code``. See also the
+ :flutter-sdk:`SyncError ` API reference.
+
+ * - ``SyncProgress.transferredBytes`` and ``SyncProgress.transferableBytes``
+ - Reported transferred and transferable values were incorrect. Consolidated
+ into a new ``SyncProgress.progressEstimate`` metric.
+ - Use ``SyncProgress.progressEstimate``. See also :ref:`sdks-check-upload-and-download-progress`.
+
+ * - ``User.provider``
+ - Provider is associated with each identity, so value was incorrect
+ for users with more than one identity.
+ - Remove any instances.
diff --git a/source/includes/aggregation-stages.rst b/source/includes/aggregation-stages.rst
index 48daa2a6e6..6c0c59d798 100644
--- a/source/includes/aggregation-stages.rst
+++ b/source/includes/aggregation-stages.rst
@@ -208,7 +208,7 @@ but replaces the array value with the array element in each copy.
#. Use ``$group`` stage with ``$addToSet`` to create new documents
for each ``type`` with a new field ``colors`` that contains an array
- of all the the colors for that flower type that occur in the collection.
+ of all the colors for that flower type that occur in the collection.
#. Use ``$unwind`` stage to create separate documents for each combination of
type and color.
#. Use ``$sort`` stage to sort the results in alphabetical order.
diff --git a/source/includes/api-details/cpp/api-not-supported-description.rst b/source/includes/api-details/cpp/api-not-supported-description.rst
new file mode 100644
index 0000000000..bfd43b8820
--- /dev/null
+++ b/source/includes/api-details/cpp/api-not-supported-description.rst
@@ -0,0 +1 @@
+This API is not currently supported in C++.
diff --git a/source/includes/api-details/cpp/api-not-supported.rst b/source/includes/api-details/cpp/api-not-supported.rst
new file mode 100644
index 0000000000..15ed2dd493
--- /dev/null
+++ b/source/includes/api-details/cpp/api-not-supported.rst
@@ -0,0 +1 @@
+The C++ SDK does not currently support this API.
diff --git a/source/includes/api-details/cpp/atlas/call-atlas-function-call-function-description.rst b/source/includes/api-details/cpp/atlas/call-atlas-function-call-function-description.rst
new file mode 100644
index 0000000000..debb2b5729
--- /dev/null
+++ b/source/includes/api-details/cpp/atlas/call-atlas-function-call-function-description.rst
@@ -0,0 +1,8 @@
+To call an Atlas Function, use the
+:cpp-sdk:`call_function() `
+member function on the ``user`` object. Pass in the name of the
+function as a string for the first parameter. This function takes two arguments,
+which we provide as a string array of arguments.
+
+The callback can provide an optional string result, or an optional error.
+In this example, we check that the result has a value.
diff --git a/source/includes/api-details/cpp/crud/create-counter-property-type-not-supported.rst b/source/includes/api-details/cpp/crud/create-counter-property-type-not-supported.rst
new file mode 100644
index 0000000000..eb8e4befcc
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-counter-property-type-not-supported.rst
@@ -0,0 +1 @@
+The C++ SDK does not currently provide a dedicated counter property type.
diff --git a/source/includes/api-details/cpp/crud/create-dictionary-property-description.rst b/source/includes/api-details/cpp/crud/create-dictionary-property-description.rst
new file mode 100644
index 0000000000..095176083d
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-dictionary-property-description.rst
@@ -0,0 +1,11 @@
+When you create an object that has a
+:cpp-sdk:`map property `,
+you can set the values for keys in a few ways:
+
+- Set keys and values on the object and then add the object to the database
+- Set the object's keys and values directly inside a write transaction
+
+.. include:: /includes/map-key-string-limitations.rst
+
+.. literalinclude:: /examples/generated/cpp/crud.snippet.percent-encode-disallowed-characters.cpp
+ :language: cpp
diff --git a/source/includes/api-details/cpp/crud/create-embedded-object-description.rst b/source/includes/api-details/cpp/crud/create-embedded-object-description.rst
new file mode 100644
index 0000000000..51bdea508d
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-embedded-object-description.rst
@@ -0,0 +1,16 @@
+To create an embedded object, assign the raw pointer of the embedded
+object to a parent object's property. Move the parent object into
+the database using the :cpp-sdk:`Realm.add() function `
+inside of a write transaction.
+
+In this example, we assign the raw pointer of the embedded object -
+``ContactDetails *`` - to the embedded object property of the parent
+object - ``Business.contactDetails``.
+
+Then, we add the ``business`` object to the database. This copies the
+``business`` and ``contactDetails`` objects to the database.
+
+Because ``ContactDetails`` is an embedded object, it does not have
+its own lifecycle independent of the main ``Business`` object.
+If you delete the ``Business`` object, this also deletes the
+``ContactDetails`` object.
diff --git a/source/includes/api-details/cpp/crud/create-geospatial-object-not-supported.rst b/source/includes/api-details/cpp/crud/create-geospatial-object-not-supported.rst
new file mode 100644
index 0000000000..0fb2bb5e45
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-geospatial-object-not-supported.rst
@@ -0,0 +1 @@
+The C++ SDK does not currently support geospatial data.
diff --git a/source/includes/api-details/cpp/crud/create-inverse-relationship-description.rst b/source/includes/api-details/cpp/crud/create-inverse-relationship-description.rst
new file mode 100644
index 0000000000..0bbfb9db40
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-inverse-relationship-description.rst
@@ -0,0 +1,11 @@
+To create an object with a inverse relationship to another object,
+assign the raw pointer of the related object to the relationship
+property of the main object. Move the object into the realm using the
+:cpp-sdk:`Realm.add() function `
+inside of a write transaction.
+
+In this example, we create two ``Person`` objects that each have a to-one
+relationship to the same ``Dog`` object. The ``Dog`` has an inverse
+relationship to each ``Person`` object. The inverse relationship backlink
+is automatically updated when a linked ``Person`` object updates its
+``Dog`` relationship.
diff --git a/source/includes/api-details/cpp/crud/create-list-property-description.rst b/source/includes/api-details/cpp/crud/create-list-property-description.rst
new file mode 100644
index 0000000000..a8251e9bfe
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-list-property-description.rst
@@ -0,0 +1,28 @@
+To create an object with a list property (to-many relationship) to one or
+more objects:
+
+- Initialize the main object and the related objects
+- Use the :cpp-sdk:`push_back
+ `
+ member function available to the SDK object lists
+ to append the raw pointers of the related objects to the main object's
+ list property
+- Move the object into the realm using the
+ :cpp-sdk:`Realm.add() function `
+ inside of a write transaction.
+
+In this example, we append the raw pointers of the related objects -
+``Employee *`` - to the relationship property of the main object
+- ``Company.employees``. This creates a one-way connection from the
+``Company`` object to the ``Employee`` objects.
+
+Then, we add the ``Company`` to the database. This copies the
+``Company`` and ``Employee`` objects to the database.
+
+The related ``Employee`` objects have their own lifecycle independent
+of the main ``Company`` object. If you delete the main object, the
+related objects remain.
+
+You can optionally create an inverse relationship to refer to the main object
+from the related object. For more information, refer to:
+:ref:`sdks-create-object-with-inverse-relationship`.
diff --git a/source/includes/api-details/cpp/crud/create-mixed-property-type-description.rst b/source/includes/api-details/cpp/crud/create-mixed-property-type-description.rst
new file mode 100644
index 0000000000..b9e2c69243
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-mixed-property-type-description.rst
@@ -0,0 +1,3 @@
+When you create an object with a ``realm::mixed`` value, you must specify the
+type of the value you store in the property. The SDK provides a ``type()``
+function you can call to determine what type of data the property has stored.
diff --git a/source/includes/api-details/cpp/crud/create-object-id-property-type-description.rst b/source/includes/api-details/cpp/crud/create-object-id-property-type-description.rst
new file mode 100644
index 0000000000..5fcb26ad20
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-object-id-property-type-description.rst
@@ -0,0 +1 @@
+Create a unique Object ID with ``realm::object_id::generate()``.
diff --git a/source/includes/api-details/cpp/crud/create-procedure.rst b/source/includes/api-details/cpp/crud/create-procedure.rst
new file mode 100644
index 0000000000..fc6935ebeb
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-procedure.rst
@@ -0,0 +1,17 @@
+#. Open a write transaction with
+ :cpp-sdk:`Realm.write() function `.
+
+#. Instantiate an unmanaged object instance within the ``realm`` namespace.
+
+#. Move the unmanaged object instance into the database using the
+ :cpp-sdk:`Realm.add() function `.
+
+ When you move an object into a database, this consumes the object as an rvalue.
+ You must use the managed object for any data access or observation. If
+ you would like to immediately work with the object, return a managed
+ version of the object.
+
+#. Work with the persisted SDK object through the returned instance. Note that
+ this *does not* apply to asymmetric objects, which are write-only. Once an
+ asymmetric object is synced, it is deleted from the database. You cannot
+ read, update, or delete an asymmetric object from the device.
diff --git a/source/includes/api-details/cpp/crud/create-realm-object-description.rst b/source/includes/api-details/cpp/crud/create-realm-object-description.rst
new file mode 100644
index 0000000000..5e9971ef9b
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-realm-object-description.rst
@@ -0,0 +1,10 @@
+To create an object, you must instantiate it using the ``realm`` namespace.
+Move the object into the database using the
+:cpp-sdk:`Realm.add() function `
+inside of a write transaction.
+
+When you move an object into a database, this consumes the object as an
+rvalue. You must use the managed object for any data access or observation.
+In this example, copying the ``dog`` object into the database consumes
+it as an rvalue. You can return the managed object to continue to work
+with it.
diff --git a/source/includes/api-details/cpp/crud/create-set-property-description.rst b/source/includes/api-details/cpp/crud/create-set-property-description.rst
new file mode 100644
index 0000000000..f061b140ce
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-set-property-description.rst
@@ -0,0 +1,5 @@
+You can create objects that contain
+:cpp-sdk:`set `
+properties as you would any SDK object, but you can only mutate a set
+property within a write transaction. This means you can only set the value(s)
+of a set property within a write transaction.
diff --git a/source/includes/api-details/cpp/crud/create-timestamp-property-type-description.rst b/source/includes/api-details/cpp/crud/create-timestamp-property-type-description.rst
new file mode 100644
index 0000000000..fa2e238356
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-timestamp-property-type-description.rst
@@ -0,0 +1,4 @@
+Use the `chrono library
+`__
+to store a ``time_point`` relative to the ``system_clock``:
+``>``
diff --git a/source/includes/api-details/cpp/crud/create-to-many-relationship-description.rst b/source/includes/api-details/cpp/crud/create-to-many-relationship-description.rst
new file mode 100644
index 0000000000..10e8989f1f
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-to-many-relationship-description.rst
@@ -0,0 +1,23 @@
+To create an object with a to-many relationship to one or more objects:
+
+- Initialize the main object and the related objects
+- Use the :cpp-sdk:`push_back
+ `
+ member function available to the Realm object lists
+ to append the raw pointers of the related objects to the main object's
+ list property
+- Move the object into the realm using the
+ :cpp-sdk:`Realm.add() function `
+ inside of a write transaction.
+
+In this example, we append the raw pointers of the related objects -
+``Employee *`` - to the relationship property of the main object
+- ``Company.employees``. This creates a one-way connection from the
+``Company`` object to the ``Employee`` objects.
+
+Then, we add the ``Company`` to the realm. This copies the
+``Company`` and ``Employee`` objects to the realm.
+
+The related ``Employee`` objects have their own lifecycle independent
+of the main ``Company`` object. If you delete the main object, the
+related objects remain.
diff --git a/source/includes/api-details/cpp/crud/create-to-one-relationship-description.rst b/source/includes/api-details/cpp/crud/create-to-one-relationship-description.rst
new file mode 100644
index 0000000000..eb228792f4
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-to-one-relationship-description.rst
@@ -0,0 +1,14 @@
+To create an object with a to-one relationship to another object,
+assign the raw pointer of the related object to the relationship
+property of the main object. Move the object into the database using
+the :cpp-sdk:`Realm.add() function `
+inside of a write transaction.
+
+In this example, we assign the raw pointer of the related object -
+``FavoriteToy *`` - to the relationship property of the main object
+- ``Dog.favoriteToy``. Then, when we add the ``dog`` object to the
+database, this copies both the ``dog`` and ``favoriteToy`` to the database.
+
+The related ``favoriteToy`` object has its own lifecycle independent
+of the main ``dog`` object. If you delete the main object, the related
+object remains.
diff --git a/source/includes/api-details/cpp/crud/create-unmanaged-copy-description.rst b/source/includes/api-details/cpp/crud/create-unmanaged-copy-description.rst
new file mode 100644
index 0000000000..e8b7e0e8f5
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-unmanaged-copy-description.rst
@@ -0,0 +1,4 @@
+Create an unmanaged version of a managed object by calling the ``detach()``
+function. This returns a value of the managed type. For example, calling
+the ``detach()`` function on a managed string property of a managed object
+returns a ``std::string`` copy of that property.
diff --git a/source/includes/api-details/cpp/crud/create-uuid-property-type-description.rst b/source/includes/api-details/cpp/crud/create-uuid-property-type-description.rst
new file mode 100644
index 0000000000..c2733a7bf6
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/create-uuid-property-type-description.rst
@@ -0,0 +1,2 @@
+You can create a UUID with ``realm::uuid()``. Or you can initialize a UUID
+with a specific value, similar to ``realm::uuid("18de7916-7f84-11ec-a8a3-0242ac120002")``.
diff --git a/source/includes/api-details/cpp/crud/delete-all-objects-not-supported.rst b/source/includes/api-details/cpp/crud/delete-all-objects-not-supported.rst
new file mode 100644
index 0000000000..8652a07994
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/delete-all-objects-not-supported.rst
@@ -0,0 +1,3 @@
+C++ does not currently provide a method to delete all objects in the database.
+You can manually iterate through the objects and delete them if you need to
+clear the database.
diff --git a/source/includes/api-details/cpp/crud/delete-all-objects-of-type-not-supported.rst b/source/includes/api-details/cpp/crud/delete-all-objects-of-type-not-supported.rst
new file mode 100644
index 0000000000..0ca1ab07aa
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/delete-all-objects-of-type-not-supported.rst
@@ -0,0 +1 @@
+C++ does not currently provide an API to delete all objects of a type.
diff --git a/source/includes/api-details/cpp/crud/delete-inverse-relationship-description.rst b/source/includes/api-details/cpp/crud/delete-inverse-relationship-description.rst
new file mode 100644
index 0000000000..22eb0a2254
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/delete-inverse-relationship-description.rst
@@ -0,0 +1,4 @@
+In this example, a ``Person`` has a to-one relationship to a ``Dog``,
+and the ``Dog`` has an inverse relationship to ``Person``.
+Setting the ``Person.dog`` relationship to ``nullptr`` removes the inverse
+relationship from the ``Dog`` object.
diff --git a/source/includes/api-details/cpp/crud/delete-multiple-objects-description.rst b/source/includes/api-details/cpp/crud/delete-multiple-objects-description.rst
new file mode 100644
index 0000000000..12d92e20f7
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/delete-multiple-objects-description.rst
@@ -0,0 +1 @@
+You can delete multiple objects within a write transaction.
\ No newline at end of file
diff --git a/source/includes/api-details/cpp/crud/delete-objects-procedure.rst b/source/includes/api-details/cpp/crud/delete-objects-procedure.rst
new file mode 100644
index 0000000000..f10dc52bc8
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/delete-objects-procedure.rst
@@ -0,0 +1,25 @@
+1. Open a write transaction with :cpp-sdk:`db.write()
+ `.
+
+#. Pass the object(s) you want to delete into the write block, or query for
+ them inside the block.
+
+ .. important:: Objects Must Be Live
+
+ You can only delete live objects. If you are working with a frozen
+ object or thread-safe reference, you must ``thaw()`` the object or
+ ``resolve()`` the thread-safe reference before deleting the object.
+ For more details, refer to :ref:`sdks-threading`.
+
+#. Call the ``remove()`` method with the object you want to delete as an
+ argument.
+
+#. The specified objects are deleted from the database and can no longer be
+ accessed or modified. If you try to use a deleted object, the SDK throws an
+ error.
+
+ If any deleted objects had a relationship with another object, the SDK
+ only deletes the reference to the other object. The referenced object
+ remains in the database, but it can no longer be queried through the deleted
+ parent property. Refer to the :ref:`sdks-delete-related-objects` section
+ for more information.
diff --git a/source/includes/api-details/cpp/crud/delete-operations-description.rst b/source/includes/api-details/cpp/crud/delete-operations-description.rst
new file mode 100644
index 0000000000..f22d8c9602
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/delete-operations-description.rst
@@ -0,0 +1,3 @@
+To delete an object from the database, pass the object to the
+:cpp-sdk:`db.remove() function `
+inside of a write transaction.
diff --git a/source/includes/api-details/cpp/crud/delete-remove-dictionary-keys-values-description.rst b/source/includes/api-details/cpp/crud/delete-remove-dictionary-keys-values-description.rst
new file mode 100644
index 0000000000..68486b7f90
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/delete-remove-dictionary-keys-values-description.rst
@@ -0,0 +1,3 @@
+To delete a :cpp-sdk:`map key
+`,
+pass the key name to ``erase()``.
diff --git a/source/includes/api-details/cpp/crud/delete-remove-elements-from-list-description.rst b/source/includes/api-details/cpp/crud/delete-remove-elements-from-list-description.rst
new file mode 100644
index 0000000000..49bf1822d3
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/delete-remove-elements-from-list-description.rst
@@ -0,0 +1,3 @@
+You can delete a :cpp-sdk:`list element
+`
+with ``erase()``, or remove all elements from the list with ``clear()``.
diff --git a/source/includes/api-details/cpp/crud/delete-remove-elements-from-set-description.rst b/source/includes/api-details/cpp/crud/delete-remove-elements-from-set-description.rst
new file mode 100644
index 0000000000..5cecc53bea
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/delete-remove-elements-from-set-description.rst
@@ -0,0 +1,3 @@
+You can delete a :cpp-sdk:`set element
+`
+with ``erase()``, or remove all elements from a set with ``clear()``.
diff --git a/source/includes/api-details/cpp/crud/delete-single-object-description.rst b/source/includes/api-details/cpp/crud/delete-single-object-description.rst
new file mode 100644
index 0000000000..f22d8c9602
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/delete-single-object-description.rst
@@ -0,0 +1,3 @@
+To delete an object from the database, pass the object to the
+:cpp-sdk:`db.remove() function `
+inside of a write transaction.
diff --git a/source/includes/api-details/cpp/crud/read-access-results-description.rst b/source/includes/api-details/cpp/crud/read-access-results-description.rst
new file mode 100644
index 0000000000..f1837b9357
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/read-access-results-description.rst
@@ -0,0 +1,7 @@
+In C++, the :cpp-sdk:`Results ` type exposes member
+functions to work with results. You may want to check the ``.size()`` of a
+results set, or access the object at a specific index.
+
+Additionally, you can iterate through the results, or observe a results
+set for changes. For more details about observing the results for changes,
+refer to :ref:`sdks-react-to-changes`.
diff --git a/source/includes/api-details/cpp/crud/read-aggregate-not-supported.rst b/source/includes/api-details/cpp/crud/read-aggregate-not-supported.rst
new file mode 100644
index 0000000000..970a6bb4e9
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/read-aggregate-not-supported.rst
@@ -0,0 +1 @@
+C++ does not currently support aggregate operators.
diff --git a/source/includes/api-details/cpp/crud/read-all-objects-of-type-description.rst b/source/includes/api-details/cpp/crud/read-all-objects-of-type-description.rst
new file mode 100644
index 0000000000..fbaafd7447
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/read-all-objects-of-type-description.rst
@@ -0,0 +1,6 @@
+To query for objects of a given type in the database, pass the object type
+``YourClassName`` to the :cpp-sdk:`db::objects\ `
+member function.
+
+This returns a :cpp-sdk:`Results ` object
+representing all objects of the given type in the database.
diff --git a/source/includes/api-details/cpp/crud/read-database-objects-procedure.rst b/source/includes/api-details/cpp/crud/read-database-objects-procedure.rst
new file mode 100644
index 0000000000..52bb42f0ac
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/read-database-objects-procedure.rst
@@ -0,0 +1,25 @@
+To find objects stored within a database:
+
+1. Query for objects of a given type in the database, pass the object type
+ ``YourClassName`` to the :cpp-sdk:`db::objects `
+ member function.
+
+#. Optionally, pass any query conditions to further refine the results:
+
+ - Specify a filter to only return objects that meet the condition. If
+ you don't specify a filter, the SDK returns all objects of the specified
+ type.
+
+ - Specify the sort order for the results.
+ Because the database is unordered, if you don't include a sort order,
+ the SDK cannot guarantee the query returns objects in any specific order.
+
+#. Work with the results. Objects may be frozen or live, depending on whether
+ you queried a frozen or live database, collection, or object.
+
+Note that any retrieved results don't actually hold matching database objects
+in memory. Instead, the database uses **direct references**, or pointers.
+Database objects in a results collection reference the matched objects, which
+map directly to data in the database file. This also means that you can
+traverse your graph of an object's :ref:`relationships `
+directly through the results of a query.
diff --git a/source/includes/api-details/cpp/crud/read-filter-or-query-objects-description.rst b/source/includes/api-details/cpp/crud/read-filter-or-query-objects-description.rst
new file mode 100644
index 0000000000..2d98d1bcd9
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/read-filter-or-query-objects-description.rst
@@ -0,0 +1,11 @@
+To filter data, call the ``.where()`` function on a collection with a valid
+query. Currently, C++ supports only a subset of RQL operators.
+
+Supported Query Operators
+`````````````````````````
+
+C++ supports the following query operators:
+
+- Equality (``==``, ``!=``)
+- Greater than/less than (``>``, ``>=``, ``<``, ``<=``)
+- Compound queries (``||``, ``&&``)
diff --git a/source/includes/api-details/cpp/crud/read-find-object-by-primary-key-not-supported.rst b/source/includes/api-details/cpp/crud/read-find-object-by-primary-key-not-supported.rst
new file mode 100644
index 0000000000..902f9c057a
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/read-find-object-by-primary-key-not-supported.rst
@@ -0,0 +1,3 @@
+C++ does not provide a dedicated API to find an object by its primary
+key. Instead, you can perform a regular query for objects where the primary
+key property matches the desired primary key value.
diff --git a/source/includes/api-details/cpp/crud/read-intro-description.rst b/source/includes/api-details/cpp/crud/read-intro-description.rst
new file mode 100644
index 0000000000..7312732687
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/read-intro-description.rst
@@ -0,0 +1,7 @@
+Query operationsn return a :cpp-sdk:`results collection
+`. Results collections may be either live or
+frozen.
+
+- **Live results** always contain the latest results of the associated query.
+- **Frozen results** represent a snapshot that cannot be modified and doesn't
+ reflect the latest changes to the database.
diff --git a/source/includes/api-details/cpp/crud/read-limit-not-supported.rst b/source/includes/api-details/cpp/crud/read-limit-not-supported.rst
new file mode 100644
index 0000000000..429456ad0c
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/read-limit-not-supported.rst
@@ -0,0 +1,3 @@
+C++ does not provide an API to limit query results. Instead, rely on the
+SDK's lazy loading characteristics to implicitly limit the objects in
+memory by only accessing the objects you need for an operation.
diff --git a/source/includes/api-details/cpp/crud/read-query-dictionary-properties-description.rst b/source/includes/api-details/cpp/crud/read-query-dictionary-properties-description.rst
new file mode 100644
index 0000000000..9880fda43d
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/read-query-dictionary-properties-description.rst
@@ -0,0 +1,3 @@
+You can iterate and check the values of a realm :cpp-sdk:`map property
+`
+as you would a standard C++ `map `__.
diff --git a/source/includes/api-details/cpp/crud/read-query-list-properties-description.rst b/source/includes/api-details/cpp/crud/read-query-list-properties-description.rst
new file mode 100644
index 0000000000..a48078c616
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/read-query-list-properties-description.rst
@@ -0,0 +1,6 @@
+You can read, query, and sort a :cpp-sdk:`list
+` similar
+to working with a :cpp-sdk:`results
+` collection. You can also
+work with a list as a results set by calling the ``as_results()`` public member
+function of the managed list class.
diff --git a/source/includes/api-details/cpp/crud/read-query-mixed-properties-description.rst b/source/includes/api-details/cpp/crud/read-query-mixed-properties-description.rst
new file mode 100644
index 0000000000..f2de21a9e2
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/read-query-mixed-properties-description.rst
@@ -0,0 +1,6 @@
+The C++ ``mixed`` data type is a union-like object that can represent a value
+any of the supported types. It is implemented using the class template
+`std::variant `__.
+This implementation means that a ``mixed`` property holds a value of
+one of its alternative types, or in the case of error - no value.
+Your app must handle the type when reading mixed properties.
diff --git a/source/includes/api-details/cpp/crud/read-query-set-properties-description.rst b/source/includes/api-details/cpp/crud/read-query-set-properties-description.rst
new file mode 100644
index 0000000000..ffff168ba6
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/read-query-set-properties-description.rst
@@ -0,0 +1,3 @@
+You can iterate, check the size of a set, and find values in a
+:cpp-sdk:`set property
+`.
diff --git a/source/includes/api-details/cpp/crud/read-sdk-results-collections-description.rst b/source/includes/api-details/cpp/crud/read-sdk-results-collections-description.rst
new file mode 100644
index 0000000000..6354a0c251
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/read-sdk-results-collections-description.rst
@@ -0,0 +1,20 @@
+The SDK's :cpp-sdk:`realm::results\ `
+collection is a struct representing objects retrieved from queries. A results
+collection represents the lazily-evaluated results of a query operation, and
+has these characteristics:
+
+- Results are immutable: you cannot manually add or remove elements to or from
+ the results collection.
+- Results have an associated query that determines their contents.
+- Results are **live** or **frozen** based on the query source. If they derive
+ from live objects, the results automatically update when the database
+ contents change. If they derive from frozen objects, they represent only a
+ snapshot and do not automatically update.
+- You cannot manually initialize an empty results set. Results can only
+ be initialized:
+
+ - As the result of a query.
+ - From a managed :ref:`list `, using the
+ :cpp-sdk:`as_results()
+ `
+ member funcion.
diff --git a/source/includes/api-details/cpp/crud/read-sort-description.rst b/source/includes/api-details/cpp/crud/read-sort-description.rst
new file mode 100644
index 0000000000..d21d66747a
--- /dev/null
+++ b/source/includes/api-details/cpp/crud/read-sort-description.rst
@@ -0,0 +1,22 @@
+Unlike using `std::sort