How to detect Realm app user has been deleted? - swift

app.currentUser.delete deletes the Realm app user from the server side. However, it seems on other logged-in devices, and the data upload is still working with no auth error.
On other logged-in devices, re-launching the app would detect that the user has been deleted. I also tried to use isLoggedIn but for authenticated devices, it seems always stays “true”.
RealmApp.shared.currentUser?.isLoggedIn
How to detect Realm app user has been deleted on other existing logged-in devices?

User presence is a challenge with Realm; it doesn’t have a baked in user presence system.
While you can monitor when a user is Online or Offline by setting a flag or var when they log in or log out (for example) - there’s no way to monitor in-between or unexpected changes of presence or deletion.
For example, a user logs in - the app can set a var (synched with the server) to ‘Online’ that other devices are observing so their UI can be updated. Whenever that flag changes, other devices or users of the app will know about it.
But what if the app crashes, the user d/c’s for whatever reason or the user force-quits the app. That var is now “Stuck” to the on position and other devices would never know they were offline.
For this use case, you can add an observer to a object in Realm that stores user status and sync's with the server. If the user logs out on one of the devices they are logged into for example, the other devices could be notified of that change. If the user is deleted, a "is deleted" flag could be set which notifies other devices or users of that change.
Expanding on that a bit - Deleting a user could trigger a server side function to then followup with perhaps removing all of their data, cleaning up references or notifying other devices of that event.

Related

Swift 4 & Firebase: onDisconnectRemoveValue fired when app go background

One of my company's app's requirements is a tracking system, where a user can track other nearby users when using our app. So there is a map and user's location (coordinates) will be updating to Firebase so other users can see on their map.
The most important thing is that we don't want sign out/offline users' location data on Firebase (because we want users only to be able to see online nearby users) so I use Firebase's onDisconnectRemoveValue() method to automatically remove disconnected users' data. The method worked great. BUT, if the app goes to the background (or screen is turned off), onDisconnectRemoveValue is fired too, so the data will be deleted (I don't want it to be deleted).
I searched the Internet, found this SO post.
This method is designed that way, I think I cannot do anything about it.
So my question is: If I want to remove a data on Firebase if the user is disconnected (loss internet connection OR app closed/killed), how can I do that?
Additional info: because I want users can updating their location to Firebase even if the App go to the background or device screen is off so I cannot check the connection at the client (can check, but when connection loss detected, cannot send remove data request to Firebase).

Are public database subscription notifications received by all users with the same subscription?

I have set up a CKSubscription* so that I receive changes to a record type.
I'm using the public database.
When I test with the same user (same iCloud account) I receive the notification.
If I use a different user (different iCloud account) I don't receive the notification.
Are subscriptions designed to only work between devices of the same user?
Here is the documentation that describes subscriptions:
Use a CKSubscription object to track changes occurring on the server.
A subscription acts like a persistent query on the server that can
track the creation, deletion, and modification of records. When
changes occur, they trigger the delivery of push notifications so that
your app can respond appropriately.
https://developer.apple.com/library/ios/documentation/CloudKit/Reference/CKSubscription_class/index.html#//apple_ref/occ/cl/CKSubscription
I would expect to receive a notification no matter which user edits the record, and subscription predicate should still match.
UPDATE
*Every user has a subscription with the same predicate, essentially focused on a single recordType with a particular property value.
The payload for this subscription is only to push shouldSendContentAvailable=YES.
The documentation reads:
When this property is YES, the server includes the content-available
flag in the push notification’s payload. That flag causes the system
to wake or launch an app that is not currently running. The app is
then given background execution time to download any data related to
the push notification, such as the set of records that changed. If the
app is already running in the foreground, the inclusion of this flag
has no additional effect and the notification is delivered to the app
delegate for processing as usual.
The recordType's security permission is:
FURTHER UPDATE
Checked the logs of the device not receiving the push when it should and saw:
apsd[85]: Silent Push: Deny app not available
Rebooted the device and now it works fine!
This behaviour was due to a mistake in how my CKSubscription was configured. If we want all users to receive notifications from the subscription then it should not have a zoneID set (should remain nil). I was setting the zoneID to the value from the defaultRecordZone, this will restrict the notifications to the current user only as the defaultRecordZone's user is the current user.
UPDATE
Finally solved the problem. Not only was it an issue of zoneID, but there also seems to be a bug. My subscription notificationInfo simply had shouldSendContentAvailable set to YES. This doesn't cause the subscription notification to fire for changes made by anyone other than the current user (u1d1->u1d2, but not u1d1>u2d2). I simply added an alertBody and the notifications start firing.
Anyone else face this issue?
UPDATE 2
Rebooting the device solved the issue.
And to add another dimension to the answers above, CKSubscriptions are logged 'per iCloud Account' not per device. All devices logged into the same iCloud Account (*) will receive all notifications based on any subscription entered by any device under that iCloud Account. When the above refers to 'user' they mean 'any device logged into that iCloud Account'.
except for the particular device that modified the file leading to the notification
Every user should create it's own subscription. A subscription is only valid for the user that created it. On app startup you should test if the subscriptions are there for the current user (you could use the database.fetchAllSubscriptionsWithCompletionHandler function for that)
Then if there is a subscription for a recordType and predicate, then that user should receive the notifications of there are any changes for records within this recorType and predicate.
There is no guarantee that every change will be pushed. If there are multiple notifications, it could happen that you won't receive all. This is why after receiving a notification you should also query for outstanding subscription notifications on the server (using CKFetchNotificationChangesOperation)
If you create a record on your device, then you should not receive a notification for that on that device. So if you have a data colleciont for a subscription in your app, then you should also update this after you modified the data.
If you think this is complex, then you could try EVCloudKitDao

How to handle multiple push notifications with user data arrived at different times?

My app is receiving APNs sent from server to Apple backend. Naturally a user may not open the app once a notification arrives to user's device. In meantime my server may push more notifications. They all contain some user data that is important when a notification is processed. So how to deal with it? iOS won't bundle and give me a batch, will it?
Here are ways how I am going to tackle it, none of which is simple.
Server keeps track of not seen data and upon arriving a new request always sends a batch of all new notifications, reflecting the count as badge count.
Client is opened by taping on notification popup. In this case it has all needed data in didReceiveRemoteNotification.
OR
Client ignores notification popup and opens app (possibly later) by tapping on app icon. In this case didReceiveRemoteNotification is not called and thus app has to fetch all needed data from server.
OR
Server never sends any user data and client always checks for new stuff every time it starts or fetches data in didReceiveRemoteNotification.
Anything else? Something simpler I am missing?
Number 4 is the right approach. There is no guarantee that any of your app code will run when an APN is received, except on iOS7. So when your app starts, it has to check with your servers for any new information that it should display.
It's simplest to code this to alway ask your servers for the latest information to display, rather than rely on the information in the APN. Use the information in the APN only to determine which new information to navigate to, so that the app displays whatever the user tapped on.
This has changed with iOS7, where you can use the remote-notification background mode to be launched whenever a push message arrives. See https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:didReceiveRemoteNotification:fetchCompletionHandler:

Call status change web-service when my App is deleted

In my application , i works with web-services and when user log in into my app my app is sending a request with status 1 means loged in and when on log out button click sending a request with status 0.
Now problem is , when user removes app from devices , status in my server is remain 1(log in) , hence other user can see him available while his app is not in device. so is there any way by which i can send request when my app removes from device (i don't think it is possible) or is there any other way that i can do in my backend side ?
Thanks in advance.
It is not possible to call a web service when the user deleting the app from an ios device. There are three methods to came to know that whether your application is there in user's ios device. But there are few limitations also.
Activate Push notification: By doing this, device will get registered with Apple's push notification service. When user delete the app from device, the registration will be revoked from APNS server and through the APNS feedback service, you can get to know whether the application is existing. (Limitation: If the user did not agree with receiving push notifications, then the app will not be registered with APNS and you never came to know that whether application is existing or not)
Activate Location Based service: If your application enables location based service, then your application will get periodic location updates in a location delegate method. In this delegate, you can call a webservice and keep update the status of user even the application is in background. (Limitation: If user disables the location update, then your server will not get info about user status)
Periodically Call a Webservice From you app: This is possible only if your app is active. (Limitation: When you application pushed in to background, your application will be in suspended mode, so it will not possible to call webservice)
Sorry Unfortunately Apple not provide any method that user Uninstall app from user's device, There is no such method.
When user delete any application device does give the alert "Do you want to delete this application" with option "Delete" and "Cancel". You don't have to write any specific code for this.
I just assume that There is one method in which you can find out when user is about to delete your app. That is you need to implement push notification Apple server will inform you when you try to push to an uninstalled instance. Other than that there's no way to detect it but i am not sure its helpful or not.
You can't do this from within the app. You would want to do something like have a periodic task which runs on the backend, checking the last activity date of logged in users and setting them to 'not available' after some configured period of inactivity. This will probably require some changes to the backend to record last activity date and a change to the app so that while it's open it sends a periodic 'heartbeat' to the backend. You probably want to make the timeout quite big (say 15 minutes, big enough to not have a large impact on performance).

Why registering for push notifications every time a user launch an app?

In the Apple documentation you can find the following sentence :
An application should register every time it launches and give its provider the current token. It calls registerForRemoteNotificationTypes: to kick off the registration process.
So when I implemented the push notification in my app I had to register the device, and I did what they said in that documentation: registering every time a user launch my app.
The token that I receive from the APNS is always the same for a given user.
My question is: why do I need to register everytime if the APNS gives me always the same token?
I read somewhere than a token can change if a user swipe his iPhone or the app. Is it the only case?
Thank you !
The token that I receive from the APNS is always the same for a given user.
Except it isn't, basically because there's nothing you can hang onto as being "a user" in the iPhone setup. The device token is always the same for each app for each device. So different apps on the same device get different tokens. The same app on two different devices gets two different tokens.
The crucial thing to note, and this is mentioned in the APNS guide, is that a user may back up their apps, settings, everything. Then they can drop their phone down the toilet. When they get their replacement phone, they can take their backup and restore it onto their new phone. Bingo - same app, same user, different device, and different token.
As far as your app is concerned, nothing has changed since the last time it ran - it doesn't know that it's actually running on a different device now. The only way it knows is because it asks for the 'current' device token, and hey presto it's a different token to last time.
You can choose to cache the token and check it against the token you just received (e.g. save it in your NSUserDefaults) - that way you don't have to communicate it back to the server unless it has changed since the last run, but you absolutely do have to check, otherwise your users will come complaining that they don't get push notifications any more since they replaced their phone.