Object must be from the Realm being queried

I’m running into what look like threading issues with Realm. I’m trying to filter a Results list of objects by another object that has a many to many relationship. I’m using a singleton RealmManager, initialized on startup, getting a user account (which is separate from a Realm.user) with that realm, and then trying filter @ObservedResults with that account object in a computed property.

I’m getting a runtime crash, “Object must be from the Realm being queried.” This error is confusing since I’m only creating one Realm, and the Realm initialization code is tagged with @MainActor. What is going on? Is @ObservedResults returning on a different thread or on a different Realm?

Also, as a side note, I cobbled this solution together from some outdated examples and pretty sparse documentation, so I’m sure I’m doing a lot wrong. Any other suggestions welcome. Thanks in advance for your help.

The code that is crashing:

struct MapView: View {
    @EnvironmentObject var user: UserAccount
    @ObservedResults(Beacon.self) var beacons: Results<Beacon>
    
    @MainActor
    var userBeacons: Results<Beacon> {
        beacons.filter("%@ IN participants", user.account!)  // Crashes here
    }
...

RealmManager:

class RealmManager: ObservableObject {
    
    let appId = "beacon1-iuaze"
    
    @Published var realm: Realm?
    static let shared = RealmManager()
    
    @MainActor
    func initialize() async throws {
        
        let app = App(id: appId)
        let user = try await app.login(credentials: Credentials.anonymous)
        
        var config = user.flexibleSyncConfiguration(initialSubscriptions: { subs in
            if subs.first(named: "all-accounts") == nil {
                subs.append(QuerySubscription<Account>(name: "all-accounts"))
            }
            if subs.first(named: "all-messages") == nil {
                subs.append(QuerySubscription<Message>(name: "all-messages"))
            }
            if subs.first(named: "beacons") == nil {
                subs.append(QuerySubscription<Beacon>(name: "beacons"))
            }
        }, rerunOnOpen: true)
        
            // Pass object types to the Flexible Sync configuration
            // as a temporary workaround for not being able to add complete schema
            // for a Flexible Sync app
        config.objectTypes = [Account.self, Beacon.self, Message.self]
        realm = try await Realm(configuration: config, downloadBeforeOpen: .always)
    }
}

Initialization

@main
struct MyApp: SwiftUI.App {
    @StateObject private var realmManager = RealmManager.shared
    @StateObject private var locationManager = LocationManager.shared
    
    var body: some Scene {
        
        WindowGroup {
            VStack {
                if let realm = realmManager.realm {
                    MainView(user: UserAccount(realm: realm))
                        .environmentObject(realmManager)
                        .environmentObject(locationManager)
                }
            }.task {
                try? await realmManager.initialize()
            }
        }
    }
}

Account


class Account: Object, ObjectKeyIdentifiable {
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted var name: String
    @Persisted var phone: String
    @Persisted var email: String
    @Persisted(originProperty: "participants") var beacons: LinkingObjects<Beacon>
    
    convenience init(name: String, phone: String, email: String) {
        self.init()
        
        self.name = name
        self.phone = phone
        self.email = email
    }
}

UserAccount

class UserAccount: ObservableObject {
    private static let EMAIL_KEY = "email"
    
    @Published var account: Account?
    
    convenience init(realm: Realm) {
        self.init()
        
        self.account = UserAccount.getAccountFromDefaults(realm: realm)
    }
    
    func setAccount(account: Account) {
        let defaults = UserDefaults.standard
        defaults.set(account.email, forKey: UserAccount.EMAIL_KEY)
        
        self.account = account
    }
    
    static func getAccountFromDefaults(realm: Realm?) -> Account? {
        let defaults = UserDefaults.standard
        guard let email = defaults.string(forKey: UserAccount.EMAIL_KEY) else {
            return nil
        }
        
        let query = realm!.objects(Account.self).where {
            $0.email == email
        }
        
        if query.count > 0 {
            return query.first
        } else {
            defaults.set("", forKey: UserAccount.EMAIL_KEY)
        }
        
        return nil
    }
}

MainView


struct MainView: View {
    @EnvironmentObject var errorHandler: ErrorHandler
    @StateObject var user: UserAccount

    var body: some View {
        if user.account != nil {
            MapView()
                .environmentObject(user)
        } else {
            AccountView()
                .environmentObject(user)
        }
    }
}

I ended up going a different direction with the code, to use a subscription instead, but I think I found the issue. Caveat that I haven’t tried this code. Basically, you need to get the Realm from the list getting filtered, find the Account object in that Realm, and then use that Account to do the filtering.

Something like:

var userBeacons: Results<Beacon> {
    let realm = beacons.realm?.thaw()
    let account = realm!.object(ofType: Account.self, forPrimaryKey: user.account._id)
    beacons.filter("%@ IN participants", account)
}