How to use database subscriptions correctly when using CloudKit sharing - ios

I'm working through how to handle CloudKit sharing and database subscriptions and I need help understanding how Apple envisions us using subscriptions and CloudKit sharing together given the properties and delegate methods we have available.
This thread is very similar but doesn't bring sharing into the equation and is a bit different functionality-wise than what I have set up.
Setting up the subs is the easy part. If there's no subscription, set one up. And if the owner stops sharing there's a delegate method that gets called so you can just delete all subs except your own. My problem comes in when a participant leaves a share. The subscription associated with that person should be deleted, but they now don't have access to the shared database to do it. And I see no way to associate a subscription with a participant ID. The only way I can figure out how to do this is basically what is mentioned in that other thread. Create a new Subscription recordType that indexes participant id to subscriptions id.
Conceptually I had this backwards and had assumed each user would be responsible for their own subs. Using a new recordType flips that so the share owner is responsible for all the subs.
Is this really the way to do it? Anyone who uses sharing runs into this so I had thought there should be some way to handle it without creating another recordType?

I misunderstood something very basic and important. For some reason I had it in my head that subs for shared databases actually lived on the owner database, and when sharing stopped they would lose access to the database and wouldn't be able to delete their own sub. That's not the case (obviously?), as you can only see your own subs, and the owner can't see any subs of participants.
When sharing stops you lose access to the CKShare record, but you still have access to your sub on the shared database. So it's trivial to delete it when needed - if the owner was responsible it would be more involved (as I was thinking above).

Related

Is there any way to transfer ownership of a CloudKit shared record?

I've just spent hours evaluating CloudKit's sharing features for a potential multi-tenancy app and, while I think I know the answer, I felt I should ask the community.
The use case: an app that supports multiple companies, each with its own users that can access shared data (records) within that company only. Imagine Slack's separate workspaces: a user of Company A should be able to see all of Company A's data, and maybe eventually Company A hires a new employee (user), who also should see that same data. Company B has completely separate users/data and is never seen by anyone from Company A.
The question: from my understanding so far of CloudKit, records are associated to a user, not a company/team/group. If User A (of Company A) shares a record with User B, that's totally doable and User B will see everything (including child records). But now let's suppose User A leaves the company. How does another user then take ownership of those records so that User B can continue to access the company's data?
It almost seems like each company would need to have its own iCloud account to serve as a master "owner", which feels unprofessional or challenging for non-techies to understand (and thereafter complicated for sharing with future users).
Am I thinking about this wrong? Am I already barking up the wrong tree by considering CloudKit as the backend for this app?
I think the most suitable setup for what you've described is to use a Public database in a CloudKit container. A CKRecord can be shared and "owned" however you want. The record remains available to all who can access the database.
Sharing a CKRecord via the CKShare functionality is much more limited. When sharing that way, you are simply providing a shared view into someone's Private database. If that user vanishes, the shared record goes with them and the sharing stops. Only the Public database can preserve a record independent of user status.
One caveat is that a Public database has to be owned by a single Apple Developer account. You can't share ownership across companies at the business level. But for the purposes of your app, your dev account can own the database and you can share the data with as many tenants as you want.
Let me know if you have any follow-up questions.

How to invite users to join a multi player Gaming Session using Parse (swift)

I'm trying to develop a trivia app, much like Quiz Up but with multi players.
Here's what I thought of doing:-
Creating a class called 'Game Session' on Parse, that has information of who created it (PFUser.current), the name of the gaming session(name), and the names of users invited(invited_users). Think of this Gaming Session as a closed group where the users interact with each other only.
So there's a createSessionViewController, and a joinSessionViewController.
If User A creates a gaming session (in createSessionViewController) and sends invites out to User B and User C, they get to accept or decline these invites in joinSessionViewController.
Now from what I have researched is that I would have to query through all the objects in the class Game Session (in viewdidload of the joinSessionViewController) and use query.wherekey for eg, User B's object id is in the column "invited_users". If so, I return that Gaming Session's object. Is that right?
If that is the case, is that an efficient way of doing it? Because it seems like if the app gets popular and there are lots of objects in the class, then it could take up a lot of time to get the one object with User B's id.
I hope I made myself clear and you guys understand my question.
PS: I'm sort of new to parse and swift, so if you could give me detailed answers it would be much appreciated.
Your logic is correct but I would also strongly suggest you take a look at Parse-LiveQuery. This tool allows you to subscribe to a PFQuery you are interested in. Once subscribed, the server will notify clients whenever a PFObject that matches the PFQuery is created or updated, in real-time.
https://github.com/ParsePlatform/parse-server/wiki/Parse-LiveQuery
https://github.com/ParsePlatform/ParseLiveQuery-iOS-OSX
Your assumption is correct and that is indeed one way you could go about doing that although it has drawbacks as you mentioned. If you felt like putting more effort into it, you can write JavaScript parse cloud code that executes after an item is saved (for example after a game session is created) and send out silent push notifications with the new objects id to the users who were invited. You could then use that push notification data to know the exact ids instead of having to query for them. This is much more advanced though. For whatever your app is, the simple route of having a model query the data on load should be fine. If you find yourself in a situation where performance is hindered due to this, well then congratulations.

Simperium multiple users accessing data

In the Simperium documentation/help section there is the following text:
All the data that is created seems like it must be tied to a user - is
that correct? Is it possible to have data that isn't tied to a user -
say a database of locations or beers?
Yes, though this isn't very clear yet. You can create a public user
(i.e., a public namespace) with an access token you share with other
users of your app so anyone can read/write to that namespace.
It's possible to limit this to read-only access as well if you need to
authoritatively publish data from a backend service.
Is there an actual example with this?
The scenario I have is as follows
My app will have a calendar
The primary user can add and remove data from the calendar
They will want to invite other users to add and remove data, my thought is that they can give them a token, the user can use their email address and this token to sign in
Am I on the right track?
You're definitely on the right track, but a little too far ahead on that track. The scenario you described is a great fit for Simperium, but sharing and collaboration features aren't yet released.
The help text you quoted is for authoritatively pushing content, for example from a custom backend to all users of your app. An example of this would be a news stream that updates on all clients as new content is added.
This is quite different than sharing calendar data among a group of users who have different access permissions, which is actually a better use of Simperium's strengths. We have a solution for this that we've tested internally, but we're using what we've learned to build a better version of it that will be more scalable for production use.
There's no timeline for this yet, but you'll see it announced on your dashboard at simperium.com.

Giving a user a 'primary key' inside their data domain

I have a rails app that consists of lots of accounts.
Inside these accounts users can create tickets.
What is the best way to give each ticket a Id that is sequential inside their account?
Obviously managing the id's myself seems to be the initial answer, but this seems to be filled with all sort of edge cases that would cause issues (for instance, two tickets writing down to the DB at once...)
I think you'll end up managing them yourself - I've implemented something similar previously, account stored 'current_ticket_id' and then when a ticket (for example) get's created it is still stored with a global PK but then an observer assigns it a friendly_ticket_id and then increments to one on the account model for the next time round. You can use the friendly_ticket_id scoped to the account via your URLs to make sure you get the right ticket back.

Allow users to remove their account

I am developing a gallery which allows users to post photos, comments, vote and do many other tasks.
Now I think that it is correct to allow users to unsubscribe and remove all their data if they want to. However it is difficult to allow such a thing because you run the risk to break your application (e.g. what should I do when a comment has many replies? what should I do with pages that have many revisions by different users?).
Photos can be easily removed, but for other data (i.e. comments, revisions...) I thought that there are three possibilities:
assign it to the admin
assign it to a user called "removed-user"
mantain the current associations (i.e. the user ID) and only rename user's data (e.g. assign a new username such as "removed-user-24" and a non-existent e-mail such as "noreply-removed-user-24#mysite.com"
What are the best practices to follow when we allow users to remove their accounts? How do you implement them (particularly in Rails)?
I've typically solved this type of problem by having an active flag on user, and simply setting active to false when the user is deleted. That way I maintain referential integrity throughout the system even if a user is "deleted". In the business layer I always validate a user is active before allowing them to perform operations. I also filter inactive users when retrieving data.
The usual thing to do is instead of deleting them from a database, add a boolean flag field and have it be true for valid users and false for invalid users. You will have to add code to filter on the flag. You should also remove all relevant data from the user that you can. The primary purpose of this flag is to keep the links intact. It is a variant of the renaming the user's data, but the flag will be easier to check.
Ideally in a system you would not want to "hard delete" data. The best way I know of and that we have implemented in past is "soft delete". Maintain a status column in all your data tables which ideally refers to the fact whether the row is active or not. Any row when created is "Active" by default; however as entries are deleted; they are made inactive.
All select queries which display data on screen filter results for only "active records". This way you get following advantages:
1. Data Recovery is possible.
2. You can have a scheduled task on database level, which can take care of hard deletes of once in a way; if really needed. (Like a SQL procedure or something)
3. You can have an admin screen to be able to decide which accounts, entries etc you'd really want to mark for deletion
4. A temperory disabling of account can also be implemented with same solution.
In prod environments where I have worked on, a hard delete is a strict No-No. Infact audits are maintained for deletes also. But if application is really small; it'd be upto user.
I would still suggest a "virtual delete" or a "soft delete" with periodic cleanup on db level; which will be faster efficient and optimized way of cleaning up.
I generally don't like to delete anything and instead opt to mark records as deleted/unpublished using states (with AASM i.e. acts as state machine).
I prefer states and events to just using flags as you can use events to update attributes and send emails etc. in one foul swoop. Then check states to decide what to do later on.
HTH.
I would recommend putting in a delete date field that contains the date/time the user unsubscribed - not only to the user record, but to all information related to that user. The app should check the field prior to displaying anything. You can then run a hard delete for all records 30 days (your choice of time) after the delete date. This will allow the information not to be shown (you will probably need to update the app in a few places), time to allow the user to re-subscribe (accidental or rethinking) and a scheduled process to delete old data. I would remove ALL information about the member and any related comments about the member or their prior published data (photos, etc.)
I am sure it changing lot since update with Data Protection and GDPR, etc.
the reason I found this page as I was looking for advice because of new Apply policy on account deletion requirements extended https://developer.apple.com/news/?id=i71db0mv
We are using Ruby on Rails right now. Your answers seem a little outdated? or not or still useful right now
I was thinking something like that
create a new table “old_user_table” with old user_id , First name, Second name, email, and booking slug.
It will allow keep all users who did previous booking. And deleted their user ID in the app. We need to keep all records for booking for audit purpose in the last 5 years in the app.
the user setup with this app, the user but never booking, then the user will not transfer to “old_user_table” cos the user never booking.
Does it make sense? something like that?

Resources