Search code examples
iosswiftrealm

Sync Realm Object Server connection completion with follow up realm object usage


I’m using Realm Object Server for a simple test project and I’m facing problems synchronizing ROS connection setup and follow up usage of the realm object to access the database. In viewDidLoad I’m calling connectROS function to initialize realmRos object/connection:

var realmRos: Realm!

override func viewDidLoad() {
    connectROS()
    if(FBSDKAccessToken.current() != nil){
        // logged in
        getFBUserData()
    }else{
        // not logged in
        print("didLoad, FB user not logged in")
    }
}   

func connectROS() {
    let username = "realm-admin"
    let password = "*********"
    SyncUser.logIn(with: .usernamePassword(username: username, password: password, register: false), server: URL(string: "http://146.185.154.***:9080")!)
    { user, error in
            print("ROS: checking user credentials")
            if let user = user {
                print("ROS: user credentials OK")
                DispatchQueue.main.async {
                    // Opening a remote Realm
                    print("ROS: entering dispatch Q main async")
                    let realmURL = URL(string: "realm://146.185.154.***:9080/~/***book_test1")!
                    let config = Realm.Configuration(syncConfiguration: SyncConfiguration(user: user, realmURL: realmURL))
                    self.realmRos = try! Realm(configuration: config)
                    // Any changes made to this Realm will be synced across all devices!
                }
            } else if let error = error {
                // handle error
                print("ROS: user check FAIL")
                fatalError(String(describing: error))
            }
    }
}

In viewDidLoad function next step is to get FB logged user (in this case I’m using FB authentication). After the logged FB user is fetched, the application perform check is that FB user is new user for my application and my proprietary ROS User’s table.

func checkForExistingProfile(user: User) -> Bool {
    var userThatExist: User?

        do {
            try self.realmRos.write() {
                userThatExist = self.realmRos.object(ofType: User.self, forPrimaryKey: user.userName)
            }
        } catch let error as NSError {
            print("ROS is not connected")
            print(error.localizedDescription)
        }

    if userThatExist != nil {
        return true
    } else {
        return false
    }
}

At this point checkForExistingProfile usually (not always) crashes at try self.realmRos.write() which happens to be nil. I think the problem comes from the synchronization between connectROS execution (which is asynchrony) and checkForExistingProfile which execution comes before connectROS completion.


Solution

  • Since you didn't show how checkForExistingProfile() is called after viewDidLoad() this is conjecture, but based on everything else you described it's the likely cause.

    What you need to do is not call checkForExistingProfile() until the sync user has logged in and your self.realmRos variable has been initialized. Cocoa Touch does nothing to automatically synchronize code written using an asynchronous pattern (like logIn(), which returns immediately but reports its actual completion state in a callback), so you need to manually ensure that whatever logIn() is supposed to do has been done before you call any additional code that depends on its completion.