Navigation

Use Realm Database with SwiftUI and Combine

On this page

  • Prerequisites
  • Overview
  • Get Started
  • Define Models
  • Views and Observed Objects
  • Integrate MongoDB Realm and Sync
  • Authenticate Users with MongoDB Realm
  • Feedback
  • Complete Code
  • Without Sync
  • With Sync
  • Have Xcode 12.2 or later (minimum Swift version 5.3.1).
  • Create a new Xcode project using the SwiftUI "App" template with a minimum iOS target of 14.0.
  • Install the iOS SDK. This SwiftUI app requires a minimum SDK version of 10.10.0.

This page contains all of the code for a working Realm and SwiftUI 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 Realm Sync. See Integrate MongoDB Realm and Sync below.

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, Combine, and SwiftUI frameworks:

import RealmSwift
import Combine
import SwiftUI
Tip

Just want to dive right in with the complete code? Jump to Complete Code below.

A common Realm data modeling use case is to have "things" and "containers of things". This app defines two related Realm object models: item and group.

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.

A group contains items. You can extend the group 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:

/// Random adjectives for more interesting demo item names
let randomAdjectives = [
"fluffy", "classy", "bumpy", "bizarre", "wiggly", "quick", "sudden",
"acoustic", "smiling", "dispensable", "foreign", "shaky", "purple", "keen",
"aberrant", "disastrous", "vague", "squealing", "ad hoc", "sweet"
]
/// Random noun for more interesting demo item names
let randomNouns = [
"floor", "monitor", "hair tie", "puddle", "hair brush", "bread",
"cinder block", "glass", "ring", "twister", "coasters", "fridge",
"toe ring", "bracelet", "cabinet", "nail file", "plate", "lace",
"cork", "mouse pad"
]
/// An individual item. Part of a `Group`.
final class Item: Object, ObjectKeyIdentifiable {
/// The unique ID of the Item. `primaryKey: true` declares the
/// _id member as the primary key to the realm.
@Persisted(primaryKey: true) var _id: ObjectId
/// The name of the Item, By default, a random name is generated.
@Persisted var name = "\(randomAdjectives.randomElement()!) \(randomNouns.randomElement()!)"
/// A flag indicating whether the user "favorited" the item.
@Persisted var isFavorite = false
/// The backlink to the `Group` this item is a part of.
@Persisted(originProperty: "items") var group: LinkingObjects<Group>
}
/// Represents a collection of items.
final class Group: Object, ObjectKeyIdentifiable {
/// The unique ID of the Group. `primaryKey: true` declares the
/// _id member as the primary key to the realm.
@Persisted(primaryKey: true) var _id: ObjectId
/// The collection of Items in this group.
@Persisted var items = RealmSwift.List<Item>()
}

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 Realm Sync is enabled.

/// The main screen that determines whether to present the SyncContentView or the LocalOnlyContentView.
/// For now, it always displays the LocalOnlyContentView.
@main
struct ContentView: SwiftUI.App {
var body: some Scene {
WindowGroup {
LocalOnlyContentView()
}
}
}

The LocalOnlyContentView has an @ObservedResults groups. This implicitly uses the default realm to load all groups when the view appears.

Tip

You can use a realm other than the default realm by passing an environment object from higher in the View hierarchy:

LocalOnlyContentView()
.environment(\.realmConfiguration, Realm.Configuration( /* ... */ ))

This app only expects there to ever be one group. If there is a group in the realm, the LocalOnlyContentView renders an ItemsView for that group.

If there is no group already in the realm, then the LocalOnlyContentView displays a ProgressView while it adds one. Because the view observes the groups thanks to the @ObservedResults property wrapper, the view immediately refreshes upon adding that first group and displays the ItemsView.

/// The main content view if not using Sync.
struct LocalOnlyContentView: View {
// Implicitly use the default realm's objects(Group.self)
@ObservedResults(Group.self) var groups
var body: some View {
if let group = groups.first {
AnyView(ItemsView(group: group))
} else {
// For this small app, we only want one group in the realm.
// You can expand this app to support multiple groups.
// For now, if there is no group, add one here.
AnyView(ProgressView().onAppear {
$groups.append(Group())
})
}
}
}

The ItemsView receives the group from the parent view and stores it in an @ObservedRealmObject property. This allows the ItemsView to "know" when the object has changed regardless of where that change happened.

The ItemsView iterates over the group'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 Realm 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.

/// The screen containing a list of items in a group. Implements functionality for adding, rearranging,
/// and deleting items in the group.
struct ItemsView: View {
/// The group is a container for a list of items. Using a group instead of all items
/// directly allows us to maintain a list order that can be updated in the UI.
@ObservedRealmObject var group: Group
/// The button to be displayed on the top left.
var leadingBarButton: AnyView?
var body: some View {
NavigationView {
VStack {
// The list shows the items in the realm.
List {
ForEach(group.items) { item in
ItemRow(item: item)
}.onDelete(perform: $group.items.remove)
.onMove(perform: $group.items.move)
}.listStyle(GroupedListStyle())
.navigationBarTitle("Items", displayMode: .large)
.navigationBarBackButtonHidden(true)
.navigationBarItems(
leading: self.leadingBarButton,
// Edit button on the right to enable rearranging items
trailing: EditButton())
// Action bar at bottom contains Add button.
HStack {
Spacer()
Button(action: {
// The bound collection automatically
// handles write transactions, so we can
// append directly to it.
$group.items.append(Item())
}) { Image(systemName: "plus") }
}.padding()
}
}
}
}

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.

/// Represents an Item in a list.
struct ItemRow: View {
@ObservedRealmObject var item: Item
var body: some View {
// You can click an item in the list to navigate to an edit details screen.
NavigationLink(destination: ItemDetailsView(item: item)) {
Text(item.name)
if item.isFavorite {
// If the user "favorited" the item, display a heart icon
Image(systemName: "heart.fill")
}
}
}
}
/// Represents a screen where you can edit the item's name.
struct ItemDetailsView: View {
@ObservedRealmObject var item: Item
var body: some View {
VStack(alignment: .leading) {
Text("Enter a new name:")
// Accept a new name
TextField("New name", text: $item.name)
.navigationBarTitle(item.name)
.navigationBarItems(trailing: Toggle(isOn: $item.isFavorite) {
Image(systemName: item.isFavorite ? "heart.fill" : "heart")
})
}.padding()
}
}

At this point, you have everything you need to work with Realm Database and SwiftUI. Test it out and see if everything is working as expected. Read on to learn how to integrate this app with Realm Sync.

Now that we have a working Realm Database app, we can optionally integrate with Realm Sync. Sync allows you to you see the changes you make across devices. Before you can add sync to this app, make sure to:

Tip

The Sync version of this app changes the app flow a bit. The first screen becomes the LoginView. When you press the Log in button, the app navigates to the ItemsView, where you see the synced list of items in a single group.

At the top of the source file, initialize an optional Realm app with your Realm app ID:

// MARK: MongoDB Realm (Optional)
// The Realm app. Change YOUR_REALM_APP_ID_HERE to your Realm app ID.
// If you don't have a Realm app and don't wish to use Sync for now,
// you can change this to:
// let app: RealmSwift.App? = nil
let app: RealmSwift.App? = RealmSwift.App(id: YOUR_REALM_APP_ID_HERE)
Tip

You can change the app reference to nil to switch back to local-only (non-Realm Sync) mode.

Let's update the main ContentView to show the SyncContentView if the app reference is not nil:

/// The main screen that determines whether to present the SyncContentView or the LocalOnlyContentView.
@main
struct ContentView: SwiftUI.App {
var body: some Scene {
WindowGroup {
// Using Sync?
if let app = app {
SyncContentView(app: app)
} else {
LocalOnlyContentView()
}
}
}
}

We define the SyncContentView below.

The SyncContentView observes the Realm app instance. The app instance is the interface to the MongoDB Realm 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.

The SyncContentView also owns an optional realm wrapped in a @State property wrapper. With Sync, we can only open the realm once a user is logged in. Once logged in, we open the realm asynchronously with asyncOpen(). The SyncContentView refreshes when a realm is eventually opened and assigned to the observable property @State var realm.

Tip

When opening a synced realm for the first time, it's a good idea to use asyncOpen() in order to download the realm completely from the backend before opening it. The device must be online to do this. If there was already a user on the device, you can use the regular Realm.init(configuration:queue:) intializer to open the realm.

This view has three possible states:

  • If the Realm app does not have a currently logged-in user, show the LoginView.
  • Otherwise, if there is not already an open realm, show a loading indicator while opening one. Ensure there is one group object in the realm.
  • Otherwise, there is a user and an open realm, so show the ItemsView for the group in the realm. Provide a LogoutButton the ItemsView can display on the top left of the navigation bar.

Here's the code for the SyncContentView:

// The main content view if using Sync.
struct SyncContentView: View {
// Observe the Realm app object in order to react to login state changes.
@ObservedObject var app: RealmSwift.App
// Observe a realm that may be opened after login.
@State var realm: Realm?
var body: AnyView {
// If there is no user logged in, show the login view.
guard let user = app.currentUser else {
return AnyView(LoginView(app: app))
}
// If logged in but the realm is not open yet, then show a progress spinner
// while opening the realm. Realm.asyncOpen() downloads the remote changes before
// the realm opens, which might take a moment.
guard let realm = realm else {
return AnyView(ProgressView() // Show the activity indicator while the realm loads
.onReceive(Realm.asyncOpen(configuration: user.configuration(partitionValue: user.id)).assertNoFailure()) { realm in
// Preload one group if it does not exist. This app only ever allows
// one group per user partition, but you could expand it to allow many groups.
if realm.objects(Group.self).count == 0 {
try! realm.write {
realm.add(Group())
}
}
// Assign the realm to the state property to trigger a view refresh.
self.realm = realm
})
}
// If logged in and the realm has been opened, then go to the items
// screen for the only group in the realm.
return AnyView(ItemsView(group: realm.objects(Group.self).first!,
leadingBarButton: AnyView(LogoutButton(app: app))))
// Pass the app to descendents via this environment object.
}
}

The LoginView maintains some state in order to display an activity indicator or error. It uses a reference to the Realm app instance passed in from above to log in when the Log in anonymously button is clicked.

Tip

In the LoginView, you can implement email/password authentication or 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 Realm app, it will notice when the user authentication state has changed and decide to show something other than the LoginView.

/// Represents the login screen. We will have a button to log in anonymously.
struct LoginView: View {
// Hold an error if one occurs so we can display it.
@State var error: Error?
// Keep track of whether login is in progress.
@State var isLoggingIn = false
// The Realm app is passed in from above
@ObservedObject var app: RealmSwift.App
var body: some View {
VStack {
if isLoggingIn {
ProgressView()
}
if let error = error {
Text("Error: \(error.localizedDescription)")
}
Button("Log in anonymously") {
// Button pressed, so log in
isLoggingIn = true
app.login(credentials: .anonymous) { result in
isLoggingIn = false
if case let .failure(error) = result {
print("Failed to log in: \(error.localizedDescription)")
// Set error to observed property so it can be displayed
self.error = error
return
}
// Other views are observing the app and will detect
// that the currentUser has changed. Nothing more to do here.
print("Logged in")
}
}.disabled(isLoggingIn)
}
}
}

The LogoutButton works just like the LoginView, but logs out instead of logging in:

/// A button that handles logout requests.
struct LogoutButton: View {
@ObservedObject var app: RealmSwift.App
@State var isLoggingOut = false
var body: some View {
Button("Log Out") {
guard let user = app.currentUser else {
return
}
isLoggingOut = true
user.logOut() { error in
isLoggingOut = false
// Other views are observing the app and will detect
// that the currentUser has changed. Nothing more to do here.
print("Logged out")
}
}.disabled(app.currentUser == nil || isLoggingOut)
}
}

Once logged in, the app follows the same flow as the local-only version. No additional changes are required to the original classes. They don't need to know whether or not you're using Realm Sync!

Did you find this guide helpful? Found an issue? Have a question? Please let us know with the feedback form on the right side of the page.

In case you would like to copy and paste or examine the complete code with or without Realm Sync, see below.

import RealmSwift
import Combine
import SwiftUI
// MARK: Models
/// Random adjectives for more interesting demo item names
let randomAdjectives = [
"fluffy", "classy", "bumpy", "bizarre", "wiggly", "quick", "sudden",
"acoustic", "smiling", "dispensable", "foreign", "shaky", "purple", "keen",
"aberrant", "disastrous", "vague", "squealing", "ad hoc", "sweet"
]
/// Random noun for more interesting demo item names
let randomNouns = [
"floor", "monitor", "hair tie", "puddle", "hair brush", "bread",
"cinder block", "glass", "ring", "twister", "coasters", "fridge",
"toe ring", "bracelet", "cabinet", "nail file", "plate", "lace",
"cork", "mouse pad"
]
/// An individual item. Part of a `Group`.
final class Item: Object, ObjectKeyIdentifiable {
/// The unique ID of the Item. `primaryKey: true` declares the
/// _id member as the primary key to the realm.
@Persisted(primaryKey: true) var _id: ObjectId
/// The name of the Item, By default, a random name is generated.
@Persisted var name = "\(randomAdjectives.randomElement()!) \(randomNouns.randomElement()!)"
/// A flag indicating whether the user "favorited" the item.
@Persisted var isFavorite = false
/// The backlink to the `Group` this item is a part of.
@Persisted(originProperty: "items") var group: LinkingObjects<Group>
}
/// Represents a collection of items.
final class Group: Object, ObjectKeyIdentifiable {
/// The unique ID of the Group. `primaryKey: true` declares the
/// _id member as the primary key to the realm.
@Persisted(primaryKey: true) var _id: ObjectId
/// The collection of Items in this group.
@Persisted var items = RealmSwift.List<Item>()
}
// MARK: Views
// MARK: Main Views
/// The main screen that determines whether to present the SyncContentView or the LocalOnlyContentView.
/// For now, it always displays the LocalOnlyContentView.
@main
struct ContentView: SwiftUI.App {
var body: some Scene {
WindowGroup {
LocalOnlyContentView()
}
}
}
/// The main content view if not using Sync.
struct LocalOnlyContentView: View {
// Implicitly use the default realm's objects(Group.self)
@ObservedResults(Group.self) var groups
var body: some View {
if let group = groups.first {
AnyView(ItemsView(group: group))
} else {
// For this small app, we only want one group in the realm.
// You can expand this app to support multiple groups.
// For now, if there is no group, add one here.
AnyView(ProgressView().onAppear {
$groups.append(Group())
})
}
}
}
// MARK: Item Views
/// The screen containing a list of items in a group. Implements functionality for adding, rearranging,
/// and deleting items in the group.
struct ItemsView: View {
/// The group is a container for a list of items. Using a group instead of all items
/// directly allows us to maintain a list order that can be updated in the UI.
@ObservedRealmObject var group: Group
/// The button to be displayed on the top left.
var leadingBarButton: AnyView?
var body: some View {
NavigationView {
VStack {
// The list shows the items in the realm.
List {
ForEach(group.items) { item in
ItemRow(item: item)
}.onDelete(perform: $group.items.remove)
.onMove(perform: $group.items.move)
}.listStyle(GroupedListStyle())
.navigationBarTitle("Items", displayMode: .large)
.navigationBarBackButtonHidden(true)
.navigationBarItems(
leading: self.leadingBarButton,
// Edit button on the right to enable rearranging items
trailing: EditButton())
// Action bar at bottom contains Add button.
HStack {
Spacer()
Button(action: {
// The bound collection automatically
// handles write transactions, so we can
// append directly to it.
$group.items.append(Item())
}) { Image(systemName: "plus") }
}.padding()
}
}
}
}
/// Represents an Item in a list.
struct ItemRow: View {
@ObservedRealmObject var item: Item
var body: some View {
// You can click an item in the list to navigate to an edit details screen.
NavigationLink(destination: ItemDetailsView(item: item)) {
Text(item.name)
if item.isFavorite {
// If the user "favorited" the item, display a heart icon
Image(systemName: "heart.fill")
}
}
}
}
/// Represents a screen where you can edit the item's name.
struct ItemDetailsView: View {
@ObservedRealmObject var item: Item
var body: some View {
VStack(alignment: .leading) {
Text("Enter a new name:")
// Accept a new name
TextField("New name", text: $item.name)
.navigationBarTitle(item.name)
.navigationBarItems(trailing: Toggle(isOn: $item.isFavorite) {
Image(systemName: item.isFavorite ? "heart.fill" : "heart")
})
}.padding()
}
}
import RealmSwift
import Combine
import SwiftUI
// MARK: MongoDB Realm (Optional)
// The Realm app. Change YOUR_REALM_APP_ID_HERE to your Realm app ID.
// If you don't have a Realm app and don't wish to use Sync for now,
// you can change this to:
// let app: RealmSwift.App? = nil
let app: RealmSwift.App? = RealmSwift.App(id: YOUR_REALM_APP_ID_HERE)
// MARK: Models
/// Random adjectives for more interesting demo item names
let randomAdjectives = [
"fluffy", "classy", "bumpy", "bizarre", "wiggly", "quick", "sudden",
"acoustic", "smiling", "dispensable", "foreign", "shaky", "purple", "keen",
"aberrant", "disastrous", "vague", "squealing", "ad hoc", "sweet"
]
/// Random noun for more interesting demo item names
let randomNouns = [
"floor", "monitor", "hair tie", "puddle", "hair brush", "bread",
"cinder block", "glass", "ring", "twister", "coasters", "fridge",
"toe ring", "bracelet", "cabinet", "nail file", "plate", "lace",
"cork", "mouse pad"
]
/// An individual item. Part of a `Group`.
final class Item: Object, ObjectKeyIdentifiable {
/// The unique ID of the Item. `primaryKey: true` declares the
/// _id member as the primary key to the realm.
@Persisted(primaryKey: true) var _id: ObjectId
/// The name of the Item, By default, a random name is generated.
@Persisted var name = "\(randomAdjectives.randomElement()!) \(randomNouns.randomElement()!)"
/// A flag indicating whether the user "favorited" the item.
@Persisted var isFavorite = false
/// The backlink to the `Group` this item is a part of.
@Persisted(originProperty: "items") var group: LinkingObjects<Group>
}
/// Represents a collection of items.
final class Group: Object, ObjectKeyIdentifiable {
/// The unique ID of the Group. `primaryKey: true` declares the
/// _id member as the primary key to the realm.
@Persisted(primaryKey: true) var _id: ObjectId
/// The collection of Items in this group.
@Persisted var items = RealmSwift.List<Item>()
}
// MARK: Views
// MARK: Main Views
/// The main screen that determines whether to present the SyncContentView or the LocalOnlyContentView.
@main
struct ContentView: SwiftUI.App {
var body: some Scene {
WindowGroup {
// Using Sync?
if let app = app {
SyncContentView(app: app)
} else {
LocalOnlyContentView()
}
}
}
}
/// The main content view if not using Sync.
struct LocalOnlyContentView: View {
// Implicitly use the default realm's objects(Group.self)
@ObservedResults(Group.self) var groups
var body: some View {
if let group = groups.first {
AnyView(ItemsView(group: group))
} else {
// For this small app, we only want one group in the realm.
// You can expand this app to support multiple groups.
// For now, if there is no group, add one here.
AnyView(ProgressView().onAppear {
$groups.append(Group())
})
}
}
}
// The main content view if using Sync.
struct SyncContentView: View {
// Observe the Realm app object in order to react to login state changes.
@ObservedObject var app: RealmSwift.App
// Observe a realm that may be opened after login.
@State var realm: Realm?
var body: AnyView {
// If there is no user logged in, show the login view.
guard let user = app.currentUser else {
return AnyView(LoginView(app: app))
}
// If logged in but the realm is not open yet, then show a progress spinner
// while opening the realm. Realm.asyncOpen() downloads the remote changes before
// the realm opens, which might take a moment.
guard let realm = realm else {
return AnyView(ProgressView() // Show the activity indicator while the realm loads
.onReceive(Realm.asyncOpen(configuration: user.configuration(partitionValue: user.id)).assertNoFailure()) { realm in
// Preload one group if it does not exist. This app only ever allows
// one group per user partition, but you could expand it to allow many groups.
if realm.objects(Group.self).count == 0 {
try! realm.write {
realm.add(Group())
}
}
// Assign the realm to the state property to trigger a view refresh.
self.realm = realm
})
}
// If logged in and the realm has been opened, then go to the items
// screen for the only group in the realm.
return AnyView(ItemsView(group: realm.objects(Group.self).first!,
leadingBarButton: AnyView(LogoutButton(app: app))))
// Pass the app to descendents via this environment object.
}
}
// MARK: Authentication Views
/// Represents the login screen. We will have a button to log in anonymously.
struct LoginView: View {
// Hold an error if one occurs so we can display it.
@State var error: Error?
// Keep track of whether login is in progress.
@State var isLoggingIn = false
// The Realm app is passed in from above
@ObservedObject var app: RealmSwift.App
var body: some View {
VStack {
if isLoggingIn {
ProgressView()
}
if let error = error {
Text("Error: \(error.localizedDescription)")
}
Button("Log in anonymously") {
// Button pressed, so log in
isLoggingIn = true
app.login(credentials: .anonymous) { result in
isLoggingIn = false
if case let .failure(error) = result {
print("Failed to log in: \(error.localizedDescription)")
// Set error to observed property so it can be displayed
self.error = error
return
}
// Other views are observing the app and will detect
// that the currentUser has changed. Nothing more to do here.
print("Logged in")
}
}.disabled(isLoggingIn)
}
}
}
/// A button that handles logout requests.
struct LogoutButton: View {
@ObservedObject var app: RealmSwift.App
@State var isLoggingOut = false
var body: some View {
Button("Log Out") {
guard let user = app.currentUser else {
return
}
isLoggingOut = true
user.logOut() { error in
isLoggingOut = false
// Other views are observing the app and will detect
// that the currentUser has changed. Nothing more to do here.
print("Logged out")
}
}.disabled(app.currentUser == nil || isLoggingOut)
}
}
// MARK: Item Views
/// The screen containing a list of items in a group. Implements functionality for adding, rearranging,
/// and deleting items in the group.
struct ItemsView: View {
/// The group is a container for a list of items. Using a group instead of all items
/// directly allows us to maintain a list order that can be updated in the UI.
@ObservedRealmObject var group: Group
/// The button to be displayed on the top left.
var leadingBarButton: AnyView?
var body: some View {
NavigationView {
VStack {
// The list shows the items in the realm.
List {
ForEach(group.items) { item in
ItemRow(item: item)
}.onDelete(perform: $group.items.remove)
.onMove(perform: $group.items.move)
}.listStyle(GroupedListStyle())
.navigationBarTitle("Items", displayMode: .large)
.navigationBarBackButtonHidden(true)
.navigationBarItems(
leading: self.leadingBarButton,
// Edit button on the right to enable rearranging items
trailing: EditButton())
// Action bar at bottom contains Add button.
HStack {
Spacer()
Button(action: {
// The bound collection automatically
// handles write transactions, so we can
// append directly to it.
$group.items.append(Item())
}) { Image(systemName: "plus") }
}.padding()
}
}
}
}
/// Represents an Item in a list.
struct ItemRow: View {
@ObservedRealmObject var item: Item
var body: some View {
// You can click an item in the list to navigate to an edit details screen.
NavigationLink(destination: ItemDetailsView(item: item)) {
Text(item.name)
if item.isFavorite {
// If the user "favorited" the item, display a heart icon
Image(systemName: "heart.fill")
}
}
}
}
/// Represents a screen where you can edit the item's name.
struct ItemDetailsView: View {
@ObservedRealmObject var item: Item
var body: some View {
VStack(alignment: .leading) {
Text("Enter a new name:")
// Accept a new name
TextField("New name", text: $item.name)
.navigationBarTitle(item.name)
.navigationBarItems(trailing: Toggle(isOn: $item.isFavorite) {
Image(systemName: item.isFavorite ? "heart.fill" : "heart")
})
}.padding()
}
}
Give Feedback

On this page

  • Prerequisites
  • Overview
  • Get Started
  • Define Models
  • Views and Observed Objects
  • Integrate MongoDB Realm and Sync
  • Authenticate Users with MongoDB Realm
  • Feedback
  • Complete Code
  • Without Sync
  • With Sync