A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://www.mongodb.com/docs/atlas/device-sdks/sdk/swift/swiftui-tutorial/ below:

Realm with SwiftUI QuickStart - Atlas Device SDKs

Tip Use Realm with SwiftUI

This page provides a small working app to get you up and running with Realm and SwiftUI quickly. If you'd like to see additional examples, including more explanation about Realm's SwiftUI features, see: SwiftUI - Swift SDK.

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:

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:

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:

import RealmSwiftimport 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 itemGroup.

An item has two user-facing properties:

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 Flexible 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 local realm.

let randomAdjectives = [    "fluffy", "classy", "bumpy", "bizarre", "wiggly", "quick", "sudden",    "acoustic", "smiling", "dispensable", "foreign", "shaky", "purple", "keen",    "aberrant", "disastrous", "vague", "squealing", "ad hoc", "sweet"]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"]final class Item: Object, ObjectKeyIdentifiable {            @Persisted(primaryKey: true) var _id: ObjectId        @Persisted var name = "\(randomAdjectives.randomElement()!) \(randomNouns.randomElement()!)"        @Persisted var isFavorite = false        @Persisted var itemDescription = ""            @Persisted(originProperty: "items") var group: LinkingObjects<ItemGroup>                @Persisted var ownerId = ""}final class ItemGroup: Object, ObjectKeyIdentifiable {            @Persisted(primaryKey: true) var _id: ObjectId        @Persisted var items = RealmSwift.List<Item>()                @Persisted var ownerId = ""}

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.

@mainstruct ContentView: SwiftUI.App {    var body: some Scene {        WindowGroup {            LocalOnlyContentView()        }    }}
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(  ))

The LocalOnlyContentView has an @ObservedResults itemGroups. This implicitly uses the default realm 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 realm, the LocalOnlyContentView renders an ItemsView for that itemGroup.

If there is no itemGroup already in the realm, 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.

struct LocalOnlyContentView: View {    @State var searchFilter: String = ""        @ObservedResults(ItemGroup.self) var itemGroups        var body: some View {        if let itemGroup = itemGroups.first {                                    ItemsView(itemGroup: itemGroup)        } else {                                                ProgressView().onAppear {                $itemGroups.append(ItemGroup())            }        }    }}
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:

@ObservedResults(MyObject.self, keyPaths: ["myList.property"])

The ItemsView receives the itemGroup 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 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 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.

struct ItemsView: View {    @ObservedRealmObject var itemGroup: ItemGroup        var leadingBarButton: AnyView?    var body: some View {        NavigationView {            VStack {                                List {                    ForEach(itemGroup.items) { item in                        ItemRow(item: item)                    }.onDelete(perform: $itemGroup.items.remove)                    .onMove(perform: $itemGroup.items.move)                }                .listStyle(GroupedListStyle())                    .navigationBarTitle("Items", displayMode: .large)                    .navigationBarBackButtonHidden(true)                    .navigationBarItems(                        leading: self.leadingBarButton,                                                trailing: EditButton())                                HStack {                    Spacer()                    Button(action: {                                                                                                $itemGroup.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.

struct ItemRow: View {    @ObservedRealmObject var item: Item    var body: some View {                NavigationLink(destination: ItemDetailsView(item: item)) {            Text(item.name)            if item.isFavorite {                                Image(systemName: "heart.fill")            }        }    }}struct ItemDetailsView: View {    @ObservedRealmObject var item: Item    var body: some View {        VStack(alignment: .leading) {            Text("Enter 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()    }}
Tip

@ObservedRealmObject is a frozen object. If you want to 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 Realm and SwiftUI. Test it out and see if everything is working as expected. Read on to learn how to integrate this app with Device Sync.

Now that we have a working Realm 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:

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 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 optional Realm app with your App ID:

let app: RealmSwift.App? = RealmSwift.App(id: YOUR_APP_SERVICES_APP_ID_HERE)
Tip

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

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

@mainstruct ContentView: SwiftUI.App {    var body: some Scene {        WindowGroup {                        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 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:

In this view, after confirming we have a user, we create a flexibleSyncConfiguration() that includes the initialSubscriptions parameter. We can use this parameter to 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.

struct SyncContentView: View {        @ObservedObject var app: RealmSwift.App    var body: some View {        if let user = app.currentUser {                                                let config = user.flexibleSyncConfiguration(initialSubscriptions: { subs in                                                if let foundSubscriptions = subs.first(named: "user_groups") {                                        return                } else {                                                                                subs.append(QuerySubscription<ItemGroup>(name: "user_groups") {                                                                        $0.ownerId == user.id                    })                    subs.append(QuerySubscription<Item>(name: "user_items") {                        $0.ownerId == user.id                    })                }            })            OpenSyncedRealmView()                .environment(\.realmConfiguration, config)        } else {                        LoginView()        }    }}

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.

Flexible 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 realm and working with the data. Sync uses this configuration to search for data that should sync to the realm.

OpenSyncedRealmView()    .environment(\.realmConfiguration, config)

Once logged in, we open the realm asynchronously with the 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 realm. If we had not provided a configuration, the property wrapper would create a default flexibleSyncConfiguration() for us, and we could subscribe to queries in .onAppear.

@AsyncOpen(appId: YOUR_APP_SERVICES_APP_ID_HERE, timeout: 4000) var asyncOpen

The OpenSyncedRealmView switches on the 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 realm is syncing. We then open the realm, passing the itemGroup to the ItemsView, or show an ErrorView if we can't open the realm.

Tip

When opening a synced realm, use the AsyncOpen property wrapper to always download synced changes before opening the realm, or the AutoOpen property wrapper to open a realm while syncing in the background. AsyncOpen requires the user to be online, while AutoOpen opens a realm even if the user is offline.

This view has a few different states:

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.

struct OpenSyncedRealmView: View {            @AsyncOpen(appId: YOUR_APP_SERVICES_APP_ID_HERE, timeout: 4000) var asyncOpen        var body: some View {                        let user = app?.currentUser        switch asyncOpen {                        case .connecting:            ProgressView()                        case .waitingForUser:            ProgressView("Waiting for user to log in...")                        case .open(let realm):            ItemsView(itemGroup: {                if realm.objects(ItemGroup.self).count == 0 {                    try! realm.write {                                                                        realm.add(ItemGroup(value: ["ownerId":user!.id]))                    }                }                return realm.objects(ItemGroup.self).first!            }(), leadingBarButton: AnyView(LogoutButton())).environment(\.realm, realm)                                case .progress(let progress):            ProgressView(progress)                        case .error(let error):            ErrorView(error: error)        }    }}

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 Flexible Sync app above, this means that the user can only read and write their own data.

Flexible 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.

ItemsView(itemGroup: {    if realm.objects(ItemGroup.self).count == 0 {        try! realm.write {                                    realm.add(ItemGroup(value: ["ownerId":user!.id]))        }    }    return realm.objects(ItemGroup.self).first!}(), leadingBarButton: AnyView(LogoutButton())).environment(\.realm, realm)

And we must also update the ItemsView to add ownerId when we create Item objects:

HStack {    Spacer()    Button(action: {                                                $itemGroup.items.append(Item(value: ["ownerId":user!.id]))    }) { Image(systemName: "plus") }}.padding()

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.

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.

struct LoginView: View {        @State var error: Error?            @State var isLoggingIn = false    var body: some View {        VStack {            if isLoggingIn {                ProgressView()            }            if let error = error {                Text("Error: \(error.localizedDescription)")            }            Button("Log in anonymously") {                                isLoggingIn = true                Task {                    do {                        let user = try await app!.login(credentials: .anonymous)                                                                        print("Logged in as user with id: \(user.id)")                    } catch {                        print("Failed to log in: \(error.localizedDescription)")                                                self.error = error                        return                    }                }            }.disabled(isLoggingIn)        }    }}

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

struct LogoutButton: View {    @State var isLoggingOut = false    var body: some View {        Button("Log Out") {            guard let user = app!.currentUser else {                return            }            isLoggingOut = true            Task {                do {                    try await app!.currentUser!.logOut()                                                        } catch {                    print("Error logging out: \(error.localizedDescription)")                }            }        }.disabled(app!.currentUser == nil || isLoggingOut)    }}

Once logged in, the app follows the same flow as the local-only version.

If you would like to copy and paste or examine the complete code with or without Device Sync, see below.

import RealmSwiftimport SwiftUIlet randomAdjectives = [    "fluffy", "classy", "bumpy", "bizarre", "wiggly", "quick", "sudden",    "acoustic", "smiling", "dispensable", "foreign", "shaky", "purple", "keen",    "aberrant", "disastrous", "vague", "squealing", "ad hoc", "sweet"]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"]final class Item: Object, ObjectKeyIdentifiable {            @Persisted(primaryKey: true) var _id: ObjectId        @Persisted var name = "\(randomAdjectives.randomElement()!) \(randomNouns.randomElement()!)"        @Persisted var isFavorite = false        @Persisted var itemDescription = ""            @Persisted(originProperty: "items") var group: LinkingObjects<ItemGroup>    }final class ItemGroup: Object, ObjectKeyIdentifiable {            @Persisted(primaryKey: true) var _id: ObjectId        @Persisted var items = RealmSwift.List<Item>()    }extension Item {    static let item1 = Item(value: ["name": "fluffy coasters", "isFavorite": false, "ownerId": "previewRealm"])    static let item2 = Item(value: ["name": "sudden cinder block", "isFavorite": true, "ownerId": "previewRealm"])    static let item3 = Item(value: ["name": "classy mouse pad", "isFavorite": false, "ownerId": "previewRealm"])}extension ItemGroup {    static let itemGroup = ItemGroup(value: ["ownerId": "previewRealm"])        static var previewRealm: Realm {        var realm: Realm        let identifier = "previewRealm"        let config = Realm.Configuration(inMemoryIdentifier: identifier)        do {            realm = try Realm(configuration: config)                                                let realmObjects = realm.objects(ItemGroup.self)            if realmObjects.count == 1 {                return realm            } else {                try realm.write {                    realm.add(itemGroup)                    itemGroup.items.append(objectsIn: [Item.item1, Item.item2, Item.item3])                }                return realm            }        } catch let error {            fatalError("Can't bootstrap item data: \(error.localizedDescription)")        }    }}@mainstruct ContentView: SwiftUI.App {    var body: some Scene {        WindowGroup {            LocalOnlyContentView()        }    }}struct LocalOnlyContentView: View {    @State var searchFilter: String = ""        @ObservedResults(ItemGroup.self) var itemGroups        var body: some View {        if let itemGroup = itemGroups.first {                                    ItemsView(itemGroup: itemGroup)        } else {                                                ProgressView().onAppear {                $itemGroups.append(ItemGroup())            }        }    }}struct ItemsView: View {    @ObservedRealmObject var itemGroup: ItemGroup        var leadingBarButton: AnyView?    var body: some View {        NavigationView {            VStack {                                List {                    ForEach(itemGroup.items) { item in                        ItemRow(item: item)                    }.onDelete(perform: $itemGroup.items.remove)                    .onMove(perform: $itemGroup.items.move)                }                .listStyle(GroupedListStyle())                    .navigationBarTitle("Items", displayMode: .large)                    .navigationBarBackButtonHidden(true)                    .navigationBarItems(                        leading: self.leadingBarButton,                                                trailing: EditButton())                                HStack {                    Spacer()                    Button(action: {                                                                                                $itemGroup.items.append(Item())                    }) { Image(systemName: "plus") }                }.padding()            }        }    }}struct ItemsView_Previews: PreviewProvider {    static var previews: some View {        let realm = ItemGroup.previewRealm        let itemGroup = realm.objects(ItemGroup.self)        ItemsView(itemGroup: itemGroup.first!)    }}struct ItemRow: View {    @ObservedRealmObject var item: Item    var body: some View {                NavigationLink(destination: ItemDetailsView(item: item)) {            Text(item.name)            if item.isFavorite {                                Image(systemName: "heart.fill")            }        }    }}struct ItemDetailsView: View {    @ObservedRealmObject var item: Item    var body: some View {        VStack(alignment: .leading) {            Text("Enter 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()    }}struct ItemDetailsView_Previews: PreviewProvider {    static var previews: some View {        NavigationView {            ItemDetailsView(item: Item.item2)        }    }}
import RealmSwiftimport SwiftUIlet app: RealmSwift.App? = RealmSwift.App(id: YOUR_APP_SERVICES_APP_ID_HERE)let randomAdjectives = [    "fluffy", "classy", "bumpy", "bizarre", "wiggly", "quick", "sudden",    "acoustic", "smiling", "dispensable", "foreign", "shaky", "purple", "keen",    "aberrant", "disastrous", "vague", "squealing", "ad hoc", "sweet"]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"]final class Item: Object, ObjectKeyIdentifiable {            @Persisted(primaryKey: true) var _id: ObjectId        @Persisted var name = "\(randomAdjectives.randomElement()!) \(randomNouns.randomElement()!)"        @Persisted var isFavorite = false        @Persisted var itemDescription = ""            @Persisted(originProperty: "items") var group: LinkingObjects<ItemGroup>                @Persisted var ownerId = ""}final class ItemGroup: Object, ObjectKeyIdentifiable {            @Persisted(primaryKey: true) var _id: ObjectId        @Persisted var items = RealmSwift.List<Item>()                @Persisted var ownerId = ""}extension Item {    static let item1 = Item(value: ["name": "fluffy coasters", "isFavorite": false, "ownerId": "previewRealm"])    static let item2 = Item(value: ["name": "sudden cinder block", "isFavorite": true, "ownerId": "previewRealm"])    static let item3 = Item(value: ["name": "classy mouse pad", "isFavorite": false, "ownerId": "previewRealm"])}extension ItemGroup {    static let itemGroup = ItemGroup(value: ["ownerId": "previewRealm"])        static var previewRealm: Realm {        var realm: Realm        let identifier = "previewRealm"        let config = Realm.Configuration(inMemoryIdentifier: identifier)        do {            realm = try Realm(configuration: config)                                                let realmObjects = realm.objects(ItemGroup.self)            if realmObjects.count == 1 {                return realm            } else {                try realm.write {                    realm.add(itemGroup)                    itemGroup.items.append(objectsIn: [Item.item1, Item.item2, Item.item3])                }                return realm            }        } catch let error {            fatalError("Can't bootstrap item data: \(error.localizedDescription)")        }    }}@mainstruct ContentView: SwiftUI.App {    var body: some Scene {        WindowGroup {                        if let app = app {                SyncContentView(app: app)            } else {                LocalOnlyContentView()            }        }    }}struct LocalOnlyContentView: View {    @State var searchFilter: String = ""        @ObservedResults(ItemGroup.self) var itemGroups        var body: some View {        if let itemGroup = itemGroups.first {                                    ItemsView(itemGroup: itemGroup)        } else {                                                ProgressView().onAppear {                $itemGroups.append(ItemGroup())            }        }    }}struct SyncContentView: View {        @ObservedObject var app: RealmSwift.App    var body: some View {        if let user = app.currentUser {                                                let config = user.flexibleSyncConfiguration(initialSubscriptions: { subs in                                                if let foundSubscriptions = subs.first(named: "user_groups") {                                        return                } else {                                                                                subs.append(QuerySubscription<ItemGroup>(name: "user_groups") {                                                                        $0.ownerId == user.id                    })                    subs.append(QuerySubscription<Item>(name: "user_items") {                        $0.ownerId == user.id                    })                }            })            OpenSyncedRealmView()                .environment(\.realmConfiguration, config)        } else {                        LoginView()        }    }}struct OpenSyncedRealmView: View {            @AsyncOpen(appId: YOUR_APP_SERVICES_APP_ID_HERE, timeout: 4000) var asyncOpen        var body: some View {                        let user = app?.currentUser        switch asyncOpen {                        case .connecting:            ProgressView()                        case .waitingForUser:            ProgressView("Waiting for user to log in...")                        case .open(let realm):            ItemsView(itemGroup: {                if realm.objects(ItemGroup.self).count == 0 {                    try! realm.write {                                                                        realm.add(ItemGroup(value: ["ownerId":user!.id]))                    }                }                return realm.objects(ItemGroup.self).first!            }(), leadingBarButton: AnyView(LogoutButton())).environment(\.realm, realm)                                case .progress(let progress):            ProgressView(progress)                        case .error(let error):            ErrorView(error: error)        }    }}struct ErrorView: View {    var error: Error            var body: some View {        VStack {            Text("Error opening the realm: \(error.localizedDescription)")        }    }}    struct LoginView: View {        @State var error: Error?            @State var isLoggingIn = false    var body: some View {        VStack {            if isLoggingIn {                ProgressView()            }            if let error = error {                Text("Error: \(error.localizedDescription)")            }            Button("Log in anonymously") {                                isLoggingIn = true                Task {                    do {                        let user = try await app!.login(credentials: .anonymous)                                                                        print("Logged in as user with id: \(user.id)")                    } catch {                        print("Failed to log in: \(error.localizedDescription)")                                                self.error = error                        return                    }                }            }.disabled(isLoggingIn)        }    }}struct LoginView_Previews: PreviewProvider {    static var previews: some View {        LoginView()    }}struct LogoutButton: View {    @State var isLoggingOut = false    var body: some View {        Button("Log Out") {            guard let user = app!.currentUser else {                return            }            isLoggingOut = true            Task {                do {                    try await app!.currentUser!.logOut()                                                        } catch {                    print("Error logging out: \(error.localizedDescription)")                }            }        }.disabled(app!.currentUser == nil || isLoggingOut)    }}struct ItemsView: View {    @ObservedRealmObject var itemGroup: ItemGroup        var leadingBarButton: AnyView?    var body: some View {        let user = app?.currentUser        NavigationView {            VStack {                                List {                    ForEach(itemGroup.items) { item in                        ItemRow(item: item)                    }.onDelete(perform: $itemGroup.items.remove)                    .onMove(perform: $itemGroup.items.move)                }                .listStyle(GroupedListStyle())                    .navigationBarTitle("Items", displayMode: .large)                    .navigationBarBackButtonHidden(true)                    .navigationBarItems(                        leading: self.leadingBarButton,                                                trailing: EditButton())                                HStack {                    Spacer()                    Button(action: {                                                                                                                                                $itemGroup.items.append(Item(value: ["ownerId":user!.id]))                    }) { Image(systemName: "plus") }                }.padding()            }        }    }}struct ItemsView_Previews: PreviewProvider {    static var previews: some View {        let realm = ItemGroup.previewRealm        let itemGroup = realm.objects(ItemGroup.self)        ItemsView(itemGroup: itemGroup.first!)    }}struct ItemRow: View {    @ObservedRealmObject var item: Item    var body: some View {                NavigationLink(destination: ItemDetailsView(item: item)) {            Text(item.name)            if item.isFavorite {                                Image(systemName: "heart.fill")            }        }    }}struct ItemDetailsView: View {    @ObservedRealmObject var item: Item    var body: some View {        VStack(alignment: .leading) {            Text("Enter 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()    }}struct ItemDetailsView_Previews: PreviewProvider {    static var previews: some View {        NavigationView {            ItemDetailsView(item: Item.item2)        }    }}

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4