When using @ObservedResults, I want to batch delete data that specifies a condition in SwiftUI

I am a first time student currently learning SwiftUI
The following code deletes the memo with empty ‘content’ just before the View is displayed with ‘.onAppear{}’ in order to display all but the ‘memo’ with empty ‘content’.

import Foundation
import RealmSwift

class Memo: Object, ObjectKeyIdentifiable {
    @Persisted(primaryKey: true) var id: ObjectId
    @Persisted var content = ""
}
import SwiftUI
import RealmSwift

struct ContentView: View {
    @ObservedResults(Memo.self) var memos
    
    @State var contentText = ""
    
    var body: some View {
        VStack {
            List {
                ForEach(memos) { memo in
                    Text(memo.content)
                        .swipeActions(edge: .trailing) {
                            Button{
                                $memos.remove(memo)
                            } label: {
                                Image(systemName: "trash")
                                    .tint(.red)
                            }
                        }
                }
            }
            .onAppear {
                let thawedEmptyMemo = memos.where{$0.content == ""}.thaw()!
                try! thawedEmptyMemo.realm?.write {
                    thawedEmptyMemo.realm?.delete(thawedEmptyMemo)
                }
            }
            TextField("Enter the contents of the memo", text: $contentText)
                .frame(width: 200)
            
            Button("Add memo") {
                let newMemo = Memo()
                newMemo.content = contentText
                $memos.append(newMemo)
            }
        }
    }
}

My questions here are.

1.Is the removal method approach correct?

2.I am using the thaw() method for the query results within ‘onAppear’, why should the object that @ObservedResults is observing be frozen?

I hope to get a good solution.

I could not find any reference to how to batch delete specific objects like this one.

Big picture here while you can do what you’re doing, a better question is does it need to be done?

In other words, every time the view appears, the code queries and deletes stuff from Realm. Is that necessary every time the view appears? Probably not.

Bigger picture: Don’t allow memo’s with empty content to be saved in the first place. That eliminates the need for that code completely.

Even if you allow empty memo’s, if it’s done correctly, when an object is removed, the view will auto-refresh itself so you don’t need to worry about where to put that code. See Observe Query Results

The Realm SwiftUI property wrappers work with frozen data to provide thread safety.

and

When you use $ to create a two-way binding, the Realm Swift SDK manages thawing the frozen objects so you can write to them.

Meaning that if your perform a quick write (using bindings) Realm manages all of the thawing and freezing for you. However, if you want to do it yourself without a quick write, you need to manage the freezing/thawing part of it.

1 Like

Also, this is a cross-post to SO.

Thanks for the response!

Why is thread safety important in SwiftUI? Would there be any defects if it were not?

Also, I thought it was a great idea to not accept ‘memo’s that are empty of content this time around in the first place.

However, if ‘memo’ could be given an expiration date and the ability to automatically delete ‘memo’ when the expiration date is reached, I don’t think we could take the approach of preventing ‘memo’ from being saved at the user input stage, as in this case (empty memos).

In that case, what approach can be taken to avoid designing a method that is executed every time the screen is displayed, as in this case?

I have removed the cross post!

Yes, there would be. High-level answer follows:

Realm objects are thread confined - meaning they only exist on the thread in which they were created (opened) so passing them across threads would be like crossing the streams* - “It would be bad.”

To work around that, objects can be frozen - which is essentially a copy of the object that can be passed around between threads without concern. However, to modify the object, it needs to be unfrozen which ‘puts’ it on the current thread so it can be safely modified.

With SwiftUI, “quick writes” can be done through bindings “$” that handle that back end nonsense for you. But you can’t do everything with quick writes so in some cases you manually need to thaw and freeze objects.

Big picture though is you should not have to worry about the UI - it should be handled automatically and with Realm, using Bindings, removing, modifying or adding an object will cause the view to re-draw and the UI to be updated (this can be through observers or the included wrappers)

The way we handle situations where fields are not allowed to be empty is (macOS) the OK button (that saves) simply is not highlighted (active) unless there’s text in the message field. That way, it’s impossible to store an object with an empty message.

Off topic:

IMO Freezing/Thawing/Actors is a gigantic pain in the butt. We spend a good 50% of our coding time either coding for that, or chasing down threading issues. There should be a better/easier way to manage this mess but it’s just something to be dealt with.

*See Ghostbusters