Calling .setPersistenceEnabled(false) when logging out of app, not working - firebase

In my Flutter/Dart mobile app I make use of Firebase RTDB persistence to enable offline use of the app.
My understanding is that to enable persistence you have to make the call, as per the following piece of code, before using any database references to eg. query the database. I use the following piece of code to enable persistence immediately after loading the app and it works fine:
FirebaseDatabase firebaseDatabase = FirebaseDatabase.instance;
bool _success = await firebaseDatabase.setPersistenceEnabled(true);
print(_success); // Prints true, so persistence is set 'on'.
When I logout of the app I attempt to turn persistence off with:
bool _success = await firebaseDatabase.setPersistenceEnabled(false);
print(_success); // Prints false, so persistence is still 'on', ie. the call failed.
I assume the reason persistence cannot be turned off is because there have been calls to db references prior to trying to switch it off.
This leads to three questions, I guess:
Should I be worried about turning it off at all, when I logout? The reason I attempt it is good house-keeping, mainly. I clean up shared preferences, close keepsyncd's, etc when logout is run. Also, though, the user can have multiple userids to login and I want to make sure that I am not retaining persisted data from their previous login id.
Related to 1, does setting persistence to false clear the cache of
data and potential queued calls to the db?
If the answers to 1 and 2 are 'yes', how can I switch persistence off given the code I'm using to do so keeps telling me it failed?

The typical way to handle this is to enable persistence once a user logs in.
Once disk persistence has been enabled and your app has used the database, it cannot be turned off. The documentation says this about it:
The returned Future will complete with true if the operation was successful or false if the persistence could not be set (because database references have already been created).
That last bit is clearly the case for you: you've been using the database already, which means that disk persistence is on.
To your specific questions:
Unfortunately the data in the local cache cannot be cleared up through the API at the moment. It is a valid feature request, but for now you'll have to assume that any data on the device can be seen by any user on that device (or device profile).
Disabling disk persistence keep the client from adding data to the cache. It does not clear any existing data in the cache.

Related

How Are Objects Synced in a Shared Realm in Swift

After scouring the documentation, I recently learned that a shared realm (globally available to all users of my app) can only be queried with Realm.asyncOpen. For example, I have a /shared realm that has read-only access to any user. I tried querying it in the usual way, but it returned zero objects. But if I query it like this, it works:
Realm.asyncOpen(configuration: sharedConfig) { realm, error in
if let realm = realm {
// Realm successfully opened
self.announcements = realm.objects(Announcement.self)
print(self.announcements)
self.tableView.reloadData()
} else if let error = error {
print(error)
}
}
This method is visibly slower than a usual realm query since it appears to be fetching the data from the server instead of a local, already-synced realm.
Does this mean that the objects pulled down are never stored in the local copy of the realm, but are queried from the ROS each time I access them?
In other words, are shared realms pulled and not synced?
a shared realm (globally available to all users of my app) can only be queried with Realm.asyncOpen
This is incorrect. If a user only has read-only access to a Realm, it must be obtained with Realm.asyncOpen. That's explicitly what the documentation you linked to states.
This method is visibly slower than a usual realm query since it appears to be fetching the data from the server instead of a local, already-synced realm.
Almost correct. Yes data is fetched from the server, but not the whole Realm from scratch. Only the new data since the last time the Realm was synced with your local copy.
Does this mean that the objects pulled down are never stored in the local copy of the realm, but are queried from the ROS each time I access them?
This synced Realm is persisted locally and will be preserved across application launches.
In other words, are shared realms pulled and not synced?
No.
Taking a step back, let's explain what's happening here.
The reason why you get a "permission denied" error if you attempt to open a read-only synced Realm synchronously is that upon initialization, a local Realm file will be created, performing write operations to write the Realm's schema (i.e. create db tables, columns & metadata) immediately. However, since the user does not have write access to the Realm, the Realm Object Server (ROS) rejects the changes and triggers your global error handler notifying you that an illegal attempt to modify the file was made by your user.
The reason why this doesn't happen with asyncOpen is that it's an asynchronous operation and therefore doesn't need to give you a valid Realm immediately, so it doesn't need to "bootstrap" it by writing the schema to it. Instead, it requests the latest state of the Realm from ROS and vends it back to you once it's fully available in its latest state at the point in time at which the call was started.
That being said, if the local copy of the Realm already has its schema initialized (i.e. after a successful asyncOpen call), and the in-memory schema defined by either the default schema or the custom object types specified in Realm.Configuration hasn't changed, then no schema will be attempted to be written to the file.
This means that any time after a successful asyncOpen call, the Realm could be accessed synchronously without going through asyncOpen as long as you're ok with potentially not having the most up to date data from ROS.
So in your case, it appears as though you only want to use asyncOpen for the very first access to the Realm, so you could persist that state (using another Realm, or NSUserDefaults) and check for it to determine whether or not to open the Realm the asynchronously or synchronously.

Hideous performance using Azure mobile services MobileServiceSyncTable

I have a mobile service sync table that is giving me absolutely HORRENDOUS performance.
The table is declared as:
IMobileServiceSyncTable<Myclass> myclassTable;
this.client = new MobileServiceClient("my url here");
var store = new MobileServiceSQLiteStore(“localdb.db”);
store.DefineTable<Myclass>();
this.client.SyncContext.InitializeAsync(store);
this.myclassTable = client.GetSyncTable<Myclass>();
Than later in a button handler I’m calling into:
this.myclassTable.ToCollectionAsync();
The problem is, the performance is horrific. It takes at best minutes and most times just sits there indefinitely.
Is there anything in the above that I’ve done that would explain why performance is so absolutely terrible?
this.myclassTable.ToCollectionAsync();
For IMobileServiceSyncTable table, the above method would execute the SELECT * FROM [Myclass] sql statement against your local sqlite db.
The problem is, the performance is horrific. It takes at best minutes and most times just sits there indefinitely.
AFAIK, when working with offline sync, we may invoke the pull operation for retrieving a subset of the server data, then insert the retrieved data into the local store table. For await this.myclassTable.PullAsync(), it would send request and retrieve the server data with the MaxPageSize in 50, and the client SDK would send another request to confirm whether there has more data and pull them automatically.
In summary, I would recommend you checking with your code to locate the specific code which causes this poor performance. Also, you could leverage adding diagnostic logging, capturing the network traces via Fiddler to troubleshoot with this issue.

Guidelines for robust synchronisation of mobile client (iOS, Swift) with Realm Object Server

I have used the techniques in the RealmTask tutorial (https://realm.io/docs/tutorials/realmtasks/ ) to get a demonstration of synchronisation with the Realm Object Server working. However, as mentioned in realm mobile platform, how to connect while offline? , it is difficult to find design guidelines on realising a robust app in the presence of intermittent network connectivity. For example, the network might not be available when the app is first run, and in the tutorial example I think the login attempt would just time out after say 30 seconds.
From various sources, I have tried to outline an implementation approach on the client and have come up with the following:
=============================================================
At start-up of app
Create login credentials with
SyncCredentials.usernamePassword()
Check whether user credentials already exist using
SyncUser.all
If so, get the correct user using the appropriate key (UserId)
If a user is obtained, get the Realm configuration using
realmConfiguration = Realm.Configuration(SyncConfiguration(user, realmURL))
Attempt a log-in with
SyncUser.logIn with SyncCredentials
On completion, put the following on the main DispatchQueue (async)
realmConfiguration = Realm.Configuration(SyncConfiguration(user, realmURL))
if not logged in, repeat login attempts every N minutes until successful? E.g. to handle the situation when the network is unavailable when the app is started, but then becomes available?
Launch the rest of the app, making realmConfiguration available.
However, only access the Realm if realmConfiguration has been set up. Design the app so that it handles the scenario of realmConfiguration not being set up.
=============================================================
Is the above approach sensible, or is there a better solution?
Katsumi from Realm here. Our RealmTasks demo application may help you.
https://github.com/realm-demos/realm-tasks/tree/master/RealmTasks%20Apple
First, check whether the user has logged in or not at launched the app.
if configureDefaultRealm() {
window?.rootViewController = ContainerViewController()
window?.makeKeyAndVisible()
} else {
window?.rootViewController = UIViewController()
window?.makeKeyAndVisible()
logIn(animated: false)
}
https://github.com/realm-demos/realm-tasks/blob/master/RealmTasks%20Apple/RealmTasks%20iOS/AppDelegate.swift#L35
If the user has been logged in before, you can use user object that was cached before. (SyncUser.current or SyncUser.all)
If there is no cached user object (The user is the first time to use the app, or the user re-installs the app), show login view to signing up/in.
The former case (Use the cached user object) doesn't require network access, so you don't need to care about the offline situation.
The latter case (The user should signing up/in) requires network access, in that case, the best practice depends on the specification of the app. It is enough to show a just alert view that indicates requiring network for some apps, or use standalone Realm and then migrate synced realm after the app will be online.

Any disadvantages to calling keepSynced on same ref multiple times?

Due to the flow of my app I'm forced to call keepSynced(true) on the same ref every time the user opens the app. I was wondering if it's bad to do so or if Firebase just ignores any redundant keepSynced() calls on the same ref.
How about calling keepSynced(true) on a sub-ref of a ref you already called keepSynced(true) on, are those ignored too?
I'm really looking for a conclusive answer.
keeySynced is either on or off for a path given by a reference. There is no "multiple keepSynced" state - that would be pointless to implement inside the SDK since there is no advantage to doing so.
You only need to call keepSynced(true) once. The way I implement it is to extend the Application Class.
public class GlobalApp extends Application {
#Override
public void onCreate() {
super.onCreate();
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
FirebaseDatabase.getInstance().getReference().keepSynced(true);
}
}
Calling keepSynced(true) on a node ensures that the Firebase Database client will synchronize that node whenever it has a connection to the database servers. There is no built-in API to keep a node synchronized when there is no such connection.
keepSynced(true);
will be useful if we enable offline support
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
If we set keepSynced(true), then whenever a user's internet connection is online, it will update it's node data. More explanation can be read in here
For example : if other user delete the node, than if another user offline. The offline user data will still exist if we're not setting the keepSynced(true).
In some case it will make a force close.
So My conclusion is, either we didn't support offline database,
or support offline but with keepSynced(true). There is also another option, we can choose whenever to keepSynced true or false.

Unable to delete information from my firebase

I was testing some changes to my firebase and accidently submitted 100's of updates to a reference. Now when I try and delete the reference it then creates a new reference with different data.
I have tried deleting everything in the firebase but it will just keep creating a new reference.
In this specific example I used set() to add 5 random values to a user name Michael. The 5 random values were called 100's of times and now when I delete the Michael user to test again it already has a value queued up and recreates itself immediately. I looked at my upload usage and it showed a huge amount of data being uploaded at one point that coincides with this error.
Any idea how to remove these queued up changes?
Make sure to disconnect the client that is writing this data. I suspect somewhere you have a process running that is generating these writes.
If you can't stop the offending process for some reason, you could always modify your security rules to deny access to the client that's doing the writes (or if it's a server using a Firebase Secret to authenticate, you could revoke its secret).
I've had a similar issue - think it has to do with your session / caching.
Try logging out of firebase and back in - if the records are still there, make a backup of your security rules, then use:
{
"rules": {
".read": false,
".write": false
}
}
and delete them.

Resources