Realm thread safe object with singleton

Viewed 5745

So I have a singleton class that manages the current user that is logged into my application.

It looks like this

class SessionManager {
    static let shared = SessionManager()

    var loggedInUser: User?
}

The problem is that if I want to get that user object from a background thread, I can't as I get the following exception:

'RLMException', reason: 'Realm accessed from incorrect thread.'

After some googling I found Thread Safe Reference's. So I changed my class to be something like this instead:

class SessionManager {
    static let shared = SessionManager()

    private var threadSafeUser: ThreadSafeReference<User>?

    var loggedInUser: User? {

        get {

            if nil === self.threadSafeUser {
                return nil
            } else {
                let realm = try! Realm()
                return realm.resolve(self.threadSafeUser!)!
            }            

        }

        set {

            if nil === newValue {
                self.threadSafeUser = nil
            } else {
                self.threadSafeUser = ThreadSafeReference(to: newValue!)
            }

        }

    }
}

My thinking was that I internally hold a reference that is thread safe, then whenever I fetch the user object, I resolve the object. However that doesn't work because you can only resolve a ThreadSafeReference once:

'RLMException', reason: 'Can only resolve a thread safe reference once.'

I also tried resolving it once inside the getter, but then you get the first issue if you reference the resolved object from a different thread to where it was resolved.

Currently I can think of two options:

  1. I keep a reference for each thread, and pass back the correct threaded user object (not 100% sure how I would do that, just a theory at the moment)

  2. I could have my set/get dispatch to main thread to get the object syncronously. so I always grab it from the main thread. I probably wouldn't be able to write changes to the object, but maybe I could read? Not sure this would actually work to be honest.

I'm 100% sure this is a problem that's been solved before, given how generic the problem is.

So fellow realm users, how should I solve this one?

Edit: Issue resolved thanks to David below. For fellow S.O users, here's what I did in the end.

class SessionManager {   
    static let shared = SessionManager()

    private var loggedInUserId: Int?

    var loggedInUser: User? {

        get {

            if nil == loggedInUserId {
                return nil
            }

            let realm = try! Realm()
            return realm.object(ofType: User.self, forPrimaryKey: loggedInUserId)

        }

        set {

            if nil == newValue {
                loggedInUserId = nil
            } else {
                loggedInUserId = newValue!.id
            }

        }

    }
}

It may need some cleaning up (the nil checks could be written better for sure), but that's the basic approach I took.

This allows me to treat the loggedInUser property just like a normal object, and whenever I read it, it grabs a new reference from realm (thread safe), and if i want to set a new user (i.e after login), I can just loggedInUser = newUser and the setter deals with how to persist it.

1 Answers
Related