Realm Swift fails in offline mode

Viewed 427

I have updated Realm (RealmSwift) in my application from Beta 10.5.1 and since this migration, my app is not working offline - actually it shows no data at all, resulting in crashing my app. Previously it was working fine.

I read through this thread, where I think the same problem is discussed, but I still could not find a solution, maybe I'm mixing up some basic logic here with login and opening a realm... https://developer.mongodb.com/community/forums/t/realm-swift-tutorial-failing-offline-first-usage/14136

I am using one email/password login and the authentication is not done via Realm, so I have one email and password combination which I use in every client.

Basically, if I'm online everything works fine, if I open the app offline, we have a problem, it does not use the local realm (which I expect as it was the case before).

let app = App(id: Constants.REALM_APP_ID)

app.login(credentials: Credentials.emailPassword(email: Constants.REALM_USERNAME, password: Constants.REALM_PASSWORD)) { (result) in
    DispatchQueue.main.sync {
        switch result {
        case .failure(let error):
            print("Failed to log in: \(error.localizedDescription)")
        case .success(let user):
            Constants.REALM = try! Realm(configuration: user.configuration(partitionValue: Constants.REALM_PARTITION_KEY))
            Constants.REALM_LOADED = true

            let recipes = Constants.REALM.objects(Recipe.self)
            Constants.REALM_NOTIF_TOKEN_RECIPES = recipes.observe { _ in
                if (Auth.UserData.user != nil && Auth.UserData.user!.isPremium) {
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: Constants.NS_RELOAD_PREMIUM_PHOTOS), object: nil) // Will invoke NS_RECIPE_CUD
                } else {
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: Constants.NS_RECIPE_CUD), object: nil)
                }
            }
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: selectorName), object: nil)
        }
        viewController?.removeSpinner()
    }
}

My code which was working in beta:

let app = RealmApp(id: Constants.REALM_APP_ID)

app.login(withCredential: AppCredentials(username: Constants.REALM_USERNAME, password: Constants.REALM_PASSWORD)) { user, error in
    DispatchQueue.main.sync {
        if let user = app.currentUser() {
            Constants.REALM = try! Realm(configuration: user.configuration(partitionValue: user.identity!))
            viewController?.removeSpinner()
            Constants.REALM_LOADED = true

            let recipes = Constants.REALM.objects(Recipe.self)
            Constants.REALM_NOTIF_TOKEN_RECIPES = recipes.observe { _ in
                if (Auth.UserData.user != nil && Auth.UserData.user!.isPremium) {
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: Constants.NS_RELOAD_PREMIUM_PHOTOS), object: nil) // Will invoke NS_RECIPE_CUD
                } else {
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: Constants.NS_RECIPE_CUD), object: nil)
                }
            }
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: selectorName), object: nil)
        } else {
            fatalError("Couldn't connect/log in to Realm")
        }
    }
}

Edit 1: Tried again with app.login and then asyncOpen. While the app is online, I nicely get all the data I want. Once I close the app, go offline and open again, I have no data received. I would have expected that it will read from the local device, offline. The code:

let app = App(id: Constants.REALM_APP_ID)

app.login(credentials: Credentials.emailPassword(email: Constants.REALM_USERNAME, password: Constants.REALM_PASSWORD)) { (result) in
    DispatchQueue.main.sync {
        switch result {
        case .failure(let error):
            print("Failed to log in: \(error.localizedDescription)")
        case .success(let user):
            let configuration = user.configuration(partitionValue: Constants.REALM_PARTITION_KEY)
            Realm.asyncOpen(configuration: configuration) { result in
                switch result {
                case .failure(let error):
                    print("Failed to open realm: \(error.localizedDescription)")
                case .success(let realm):
                    Constants.REALM = realm
                }
            }
        }
    }
}

When tracing out it goes into the app.login > .failure branch, and the error is: The Internet connection appears to be offline.

Edit 2: Tried using try! Realm if the app.currentUser is set (I assume it means that the realm is locally available as the user has previously logged in while it was online). But still, I get a crash in the app as no data is fetched/read.

let app = App(id: Constants.REALM_APP_ID)

if let user = app.currentUser {
    // I enter here when the app is offline but actually my data is not fetched.
    let configuration = user.configuration(partitionValue: Constants.REALM_PARTITION_KEY)
    Constants.REALM = try! Realm(configuration: configuration)
    viewController?.removeSpinner()
} else {
    app.login(credentials: Credentials.emailPassword(email: Constants.REALM_USERNAME, password: Constants.REALM_PASSWORD)) { (result) in
        DispatchQueue.main.sync {
            switch result {
            case .failure(let error):
                print("Failed to log in: \(error.localizedDescription)")
            case .success(let user):
                let configuration = user.configuration(partitionValue: Constants.REALM_PARTITION_KEY)
                Realm.asyncOpen(configuration: configuration) { result in
                    switch result {
                    case .failure(let error):
                        print("Failed to open realm: \(error.localizedDescription)")
                    case .success(let realm):
                        Constants.REALM = realm
                    }
                    viewController?.removeSpinner()
                }
            }
        }
    }
}
1 Answers

After several tries I could get it working properly.

viewController?.showSpinner(onView: viewController!.view)
let app = App(id: Constants.REALM_APP_ID)

app.login(credentials: Credentials.emailPassword(email: Constants.REALM_USERNAME, password: Constants.REALM_PASSWORD)) { (result) in
    DispatchQueue.main.sync {
        switch result {
        case .failure(let error):
            // Might be because of no internet connection!
            print("Failed to log in: \(error.localizedDescription)")
            if let user = app.currentUser {
                // If the user was logged in before, we use its configuration value.
                let configuration = user.configuration(partitionValue: Constants.REALM_PARTITION_KEY)
                Constants.REALM = try! Realm(configuration: configuration)
            }
        case .success(let user):
            Constants.REALM = try! Realm(configuration: user.configuration(partitionValue: Constants.REALM_PARTITION_KEY))
        }
        viewController?.removeSpinner()
    }
}
Related