CKReference .DeleteSelf attribute has no effect - ios

How does .DeleteSelf really work? Docs says:
When the reference object’s action is set to
CKReferenceActionDeleteSelf, the target of the reference—that is, the
record stored in the reference’s recordID property—becomes the owner
of the source record. Deleting the target (owner) record deletes all
its source records.
but my impression is that deleting a target will not always delete source. And it is quite annoying when it remains in the container, client downloads it, and expect that the reference point to somewhere, but target does not exist when building up slice of the server data store on client?
How do you treat this case? You ignore that sort of records? Or periodically you look up the CloudKit storage, searching for corrupt records to delete them?
Or instead of deleting a record is it better to set an attribute that it is in a deleted state, but keep it in the database?

I just struggled with this one for a while and I thought I would share my findings...
It is fundamentally a permission issue. The cascading delete will only work if the user deleting the records has 'write' permissions to all the records needing to be deleted.
So in the CloudKit Dashboard, the cascading delete will only work for the records created with the developer's iCloud account.
If you need to delete records that don't belong to the user deleting them, you can add 'write' permissions for a Record Type under Security.

If you are deleting via CloudKit Dashboard you have to wait before switching record types to check the other end of the reference. More than likely you switched before the delete actually happened. You can use Safari's Web Inspector on the Network tab to check when the delete has actually finished. It takes a very long time to delete multiple records.

Related

CloudKit Delete Self option for CKReference Doesn't work

Does anybody have experience using the CloudKit option to DeleteSelf for a CKReference? Here is what I got from the docs:
DeleteSelf
The delete action for referenced records. Deleting a record also deletes any records containing CKReference objects that point to that record. The deletion of the additional records may trigger a cascade deletion of more records. The deletions are asynchronous in the default zone and immediate in a custom zone.
So I've been testing this out, I have created multiple CKReference objects both using the CloudKit Dashboard and by using my app. Whenever I do it with my app I create the reference like so:
let reference:CKReference = CKReference(recordID: savedFriend.friendID, action: CKReferenceAction.DeleteSelf)
I know that it is being assigned to delete itself, because in the dashboard it shows the option checked off:
I must be doing something wrong because whenever I go and manually delete that record that is referenced using the dashboard, or set my app to delete it programmatically, the record disappears but the reference never gets removed. I thought maybe I should just wait and it would happen eventually, but hours later it's still there checked off like it should have deleted itself. It's kind of frustrating because I designed some of my code assuming this would work, now I can have an app that's wasting resources trying to find a CKRecord that doesn't exist anymore. Should I just not rely on this to ever work? I appreciate any help or advice I can get.
UPDATE ~ 04/07/2016
I jumped the gun coming back here and posting an update that the issue had resolved itself. I wasn't totally convinced that the issue wouldn't pop back up so I sought some reassurance last night. I created 5 different CKRecords and created various different CKReference objects to reference them throughout my database. I then proceeded to set the CKReferenceAction.DeleteSelf option on each reference (I tried two manually through the dashboard and the other three done programmatically). Anyways, I waited a few minutes and then deleted the referenced records...after ten minutes the references were all still present. I waited another 5-10 minutes and they were still there so I went to bed assuming they should definitely be gone by morning, right? Wrong! Twelve hours later the CKReference entries are still there and the referenced records are still gone. I'm definitely scratching my head, it seems as though it's a bug with CloudKit. I should mention that I've also noticed some weird behavior in my dashboard. For the past four or so days, down in the bottom left hand corner it has consistently said it's "Reindexing development data", here is a picture for example:
Could that be causing this issue? Is anybody familiar with this issue and knows a way to solve this? Any suggestions would be appreciated. I have filed a bug report with Apple.
This is most likely a permission issue.
The cascading delete will only work if the user deleting the records has 'write' permissions to all the records needing to be deleted. So in the CloudKit Dashboard, the cascading delete will only work for the records created with the developer's iCloud account.
See my answer there: https://stackoverflow.com/a/38606004/6643055

Core Data: change delete rule programmatically

My iOS app downloads record from a 3rd party database, and stores them locally using CoreData. The user is able to edit some aspects of the stored records, eg user notes can be added in addition to notes from the database.
Occasionally, the database gets updated, and I provide a refresh function. Instead of checking what parts of the entries are different, I just brute-force remove the old one and replace it with a new one.
However, this also removes the user notes. I tried saving them before the refresh, and re-adding them after inserting the new entry, but once the original entry gets deleted, the user note is also deleted because of the "Cascade" delete rule. If I set the delete rule to "No Action" for notes, then all notes will not be deleted.
So I was thinking, is it possible to temporarily change the delete rule of the user note while updating so that it doesn't get deleted with the old entry?
Or maybe my approach is completely wrong, and there are better ways to handle this?
UPDATE: I have created a follow up question here: Change relationship of NSManagedObject to different context
You are not allowed to change model after it was instantiated, except versioning.
The way I think, you should create new entity, say, CustomNote and store some unique identifier to original "record". Then just retrieve this notes by id.
Although, it may be some more advanced approach with relationships, this is the simplest.

cloudkit sync with local database

I save quite a bit of data to a mysql database on the phone, then upload when we have wifi. I am syncing about 6 tables and have it working properly, need to add more error checking, but it is working with some wait statements thrown in where I need them.
The problem is I am doing each update separately with a request and when the request comes back I don't have the information that I need to delete that entry from the local database, so that it doesn't re-upload duplicate information. Is there a way to save a variable to each upload so that when it comes back I can delete that entry? I can do that with one variable. It tells me the record I updated, but they are all referenced items to the owner and I can't find the info I need. I will be doing about 100 uploads at a time.

ABAddressbook notification of delete of Record

I use the kABpersonModificationDateProperty to detect AddressBook records that have been added/modified since the last startup.
Is it possible to detect which records have been deleted since my last startup?
I dearly wish it were so.
Your only option here is to cache all the record IDs, and then compare the full list at startup.
You're still exposed to situations where iOS rewrites all the IDs (such as when syncing via iCloud), in which case it will appear that all the records were deleted and an entirely new batch added. You can try to recognize cases where the ID was changed on an existing record by comparing fields (such as first/last name).

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