I have an iOS app powered by a Realm Object Server hosted on Amazon and I am curious as to when syncing actually occurs. Is syncing lazy on queries, real-time for all open realms, or something else?
For example, let's say I have a realm called /common and all SyncUsers have read/write permissions to this realm. Each active SyncUser also has a notification block listening to a GlobalProfile object in the /common realm corresponding to their SyncUser identity. If one user makes changes to another user's GlobalProfile object, will all users download this change immediately, or will only the user with the notification block on this object immediately download the change?
Specifically, I want to create a way for a user (let's name it Tom) to search for other users and send them read/write permissions to Tom's realm. My current solution is a public realm in which each user adds a GlobalProfile object. To search for a user named Jerry, Tom can simply query the public realm. To grant Jerry read/write permissions to Tom's realm, Tom can write a SyncPermissionOffer.token into Jerry's GlobalProfile. Because Jerry has a notification block listening to his GlobalProfile, he will be immediately notified of this token and can accept the SyncPermissionOffer to Tom's realm. My worry, however, is that all users, not just Jerry, will sync this change in the /common realm, which is unnecessary. If the app has 100,000 users, I don't want each user to constantly sync all SyncPermissionOffer tokens being sent between other users.
Please let me know if this question is not clear. Thank you so much for the help!
Sync is real-time on all changes. Sync is not waiting for, or contingent on, a notification block. The notification block is executed locally and is not used to alter the client-server communication in any way.
If you have a /common Realm, it will contain all your permission offers and
all users will download all other users permission offers. It will grow large over time.
What you could do is to have a /common Realm that only contains the user ids, their "names", and a path to their "inbox" Realm. When user A wants to
invite user B, A looks up B in /common, obtains the Realm path for B's inbox, and sends a permission offer to that.
With this approach, the common Realm would not grow that big, because the data par user could be kept below 100 bytes, say.
Related
From several Apple WWDC talks on CloudKit, when using CloudKit to sync private user data across devices, when the user logs out of iCloud, the App is supposed to empty the on-device cache / local replica (I use Core Data for on-device permanence). Then when this user (or any user really) logs back into his respective iCloud account on that same device, the device is then supposed to sync back down the data from the corresponding iCloud account. Makes sense!
I was wondering how I can achieve this when I use NSPersistentCloudKitContainer (instead of writing all that CloudKit code myself). I looked at the sample code related to Apple WWDC 2019 session 202 ("Using Core Data with CloudKit"), and this code does not what I want (the sample just does not focus on the iCloud account part, that is why they do not bother to empty the cache, I think): Indeed the NSPersistentCloudKitContainer stops synching if I log out the user, but it picks up again synching when I log back in. That is actually more like the desired (and indeed also actual, which is good) behaviour by the sample App when my web connection goes offline for a while.
But if the user logs out of iCloud (I use CloudKit framework to be informed about user account logout and login status), what I would need to do is somehow:
"disconnect" the sqlite store from the NSPersistentCloudKitContainer, then
empty (or even delete) the database, and then,
when the old (or another) user logs in, initialise a new NSPersistentCloudKitContainer (that will recreate an empty database as if the App would be used for the first time ever), so communication with the respective Cloud database can proceed.
I can not just empty the database WHILE being "connected" to the NSPersistentCloudKitContainer, as it records all the deletes and as soon as the (same) user logs in again, these deletes are synched to the Cloud, which I of course do NOT want.
Is this how one does it, aka essentially follow these three steps?
And if yes, how do I do step 2? Since I must use some kind of NSPersistentContainer to talk to the database, Apple warns not to directly do file system operations but only via Core Data. So do I need to init a "standard" NSPersistentContainer (without a communication pipe to the Cloud), and then destroy the database (e.g. using e.g. the coordinator instance method destroyPersistentStore())?
Or is there another approach for achieving the same thing?
Thank you very much for any help!!
I have an iOS app which uses Realm locally and it works great, and my intention is to use Realm Object Server to enable:
Data sync across devices
Data restoration on app reinstall
I've been having a lot of trouble with the last one. The flow in my app is the following:
First, it tries to find a User in my Realm Sync. If a User exists, go to step 4. Otherwise, go to step 2;
Ask for the user's name, income and payday;
Get the user's userRecordID for iCloud and logIn to Realm Object Server.
Use the SyncUser's identity as the User entity identifier. Save the data I just asked (name, income and payday) to Realm;
Go to main app screen and let the user enjoy the app.
ROS is using cloudkit based authentication. The problem occurs if I follow steps 1-4 above, and then reinstall the app. I expected the previously created User to be retrieved in step 1, but instead all of its fields beside the primary key are set to blank values (name becomes empty string, income and payday become 0). Is this behavior expected? Is what I want to do within the purpose of Realm?
EDIT
Made the flow clearer after #AustinZ's answer.
"Data restoration on app install" is definitely a valid use case for Realm Sync. However, the flow you describe is problematic.
A synchronized Realm is identified by two pieces of information: a sync user and the Realm's path on the Realm Object Server (e.g. /~/my/realm). If you have the same sync user logged into multiple devices, and they each open a copy of a Realm at the same path, they are opening the same synced Realm and will stay in sync with each other.
However, to get the same sync user, a user needs to log in to the Realm Object Server from their device. You do this by creating a CloudKit SyncCredentials value using SyncCredentials.cloudKit(token:) and passing that credential to SyncUser.logIn(). The device will then communicate with the Realm Object Server, and if the log in succeeds, then the logIn() method's callback block will be invoked and will give you a SyncUser. That is the sync user you need to use to open Realms.
So:
You shouldn't be saving users or user info to Realms manually. We persist the relevant information automatically.
If a user deletes and re-installs their app, the app's documents and files are deleted as well, which means you shouldn't assume you can access a Realm's data after the app which created it was re-installed.
What you should be doing is, in step 3, using the user's CloudKit credentials and logging into the Realm Object Server as described above.
Please review our documentation for more information.
I'm moving an app from using CloudKit sync to using the Realm Mobile Platform.
I want the login process to be transparent to the user, so I'm using CloudKit authentication, this way they don't have to worry about creating an account or remembering a password. Of course if the user doesn't have an iCloud account set up, the data will not sync, but that's how the app is already working without realm: we just alert the user to the fact that the data will not sync because an iCloud account could not be found.
I noticed in the documentation that in order to open the synchronized realm, I have to provide the user's credentials.
My question is: how do I handle the case where the first time the user launches the app he doesn't have internet connection or doesn't have an iCloud account setup? I would like to just store the data locally if the user doesn't have an iCloud account (this is how the app works currently), but if I understand it correctly, the only way I can open the synchronized realm is to have the user's credentials, which I need internet access and an iCloud account to be set up on the device to get. Is there an easy way to handle that case with realm?
I know I could have a separate offline storage and move its data to the synced realm once on-line, but that would be tricky to get right and quite a bit of work.
Unfortunately you are correct: the very first time the user logs in to the Realm Object Server (via +[SyncUser logInWithCredentials:...]), the user needs to already have whatever identity provider account they're going to use (in your case, an iCloud account), as well as Internet connectivity.
Once the user's logged in at least once, their Realm credentials are persisted internally and the user can be used subsequently to open up Realms immediately, even if there is no connectivity.
The best workaround right now is to use a non-synced Realm to store whatever data you need before you have an opportunity to log in, and then manually copy the data over to your synchronized Realm once you are able to log in the user successfully and open the synced Realm with the user.
We know this is a pain point for a lot of our users, and plan to address this deficiency in the near future with features to let you immediately start using 'synced' Realms before a user has logged in.
I know that Realm offers two way syncing and can trigger server-side events when data changes.
I am making a sort of messaging app, my question is how would I implement it so that when someone writes a message (which will update the DB), how does this message immediately display on other devices?
All you have to do is to open the same Realm on all devices, then they will all see the changes instantly as they happen (as long as they are connected of course :-)).
Check out the tutorial, it shows the data being realtime synced between both an iOS app, a Mac app and the Realm Browser: https://realm.io/docs/tutorials/realmtasks/
I'm one of the developers working on Realm Tasks, incorporating the latest features of the Realm Mobile Platform into it. :)
Making a chat app has become significantly easier with the release of client Realm sharing in Realm 2.3.
To make a chat app, the logic flow would be something like this:
A user starts a new chat room. This would be represented in RMP as a single, private Realm file belonging to that user.
The user can then invite other users access to that chat room via the sharing mechanism.
Every user can then contribute messages. Each message would be a separate Realm Object written to the shared Realm.
Each time a new chat message object is written to the Realm, it would be synchronized with every other user in the room.
Please let me know if you need any additional clarification.
My iOS/Parse app allows multiple users to modify data which is shared among everyone. So if user 1 modifies the data I want the rest of the users to be notified of the change so that they can request the new data. I'd like this to happen in the background. So, if I have the app open and other user makes a change I want to get those changes as soon as possible (kind of like a shared notes app).
Does Parse support this type of data modification notification? If it doesn't I'd have to do something ugly like periodically poll for changes. :-(