CareKit icon indicating copy to clipboard operation
CareKit copied to clipboard

Delete OCKContact?

Open chriisong opened this issue 4 years ago • 5 comments

I am trying to allow users to select and delete individual contact object.

When user creates a contact, I initiate the contact object with var ockContact = OCKContact(id: UUID().uuidString, givenName: givenName, familyName: familyName, carePlanUUID: nil)

and then:

let appD = UIApplication.shared.delegate as! AppDelegate
appD.coreDataStore.addContacts([ockContact])

And when then to delete, I query with the uuid that I save on Core Data object along with the OCKStore:

let query = OCKContactQuery(id: cd_contact.uuidString) 

// cd_contact = NSManagedObject of the selected contact that user wants to delete

self.storeManager.store.fetchAnyContact(withID: cd_contact.uuidString, callbackQueue: .main) { result in
        switch result {
        case .failure(let error): // handle error
        case .success(let contact):
            self.storeManager.store.deleteAnyContact(contact)
         }

I'm not sure what I'm missing but the contact doesn't get deleted... It doesn't get fetched once these lines of code run (i put them in viewDidLoad() of my OCKContactsListViewController). So that MUST mean that the contact did in fact get deleted from the store, right? So why does it still show up in my OCKContactsListViewController?

Please advise. Thank you

chriisong avatar Sep 22 '20 02:09 chriisong

For OCKContact, I've found a workaround. I would still love to learn how to delete contacts so that OCKContactListViewController's private fetchContacts doesn't include the ones I had deleted.

Same issue with OCKDailyPageViewController. I want users to delete (or exclude) tasks that they do not want to track anymore. I can delete the task (similar to how I described deleting contacts above), but the weekCalendarPageViewController's progress still accounts for the task that the user deleted. So if there were originally 2 adherences with one task, and I delete the task and create another task with 2 adherences, then the total adherence becomes 4.

All in all, I just wish I can delete contacts, tasks, or what have you and be reflected on the UI that I use. I thought that was the whole point of using synchronizedStoreManager: to sync the UI with the data from the store.

I am really new to this library, and I am so thankful for how much CareKit allows me to do. I just wish to know more so that I can do the best I can for my users.

Thank you very much for all the amazing work you guys do.

chriisong avatar Sep 22 '20 08:09 chriisong

Sorry to hear you've been running into issues!

Based on the code you've provided, the contact query is the issue. That initializer takes an id rather than a uuid:

let query = OCKContactQuery(id: contact.id) 

The id is a human readable string that identifies a contact, no matter the version. The uuid is a unique identifier for an entity in the CareKit store.

I would guess that the same issue is happening when attempting to delete tasks, but if you share the code we can do some debugging!

gavirawson-apple avatar Sep 22 '20 15:09 gavirawson-apple

TLDR; The contact list view controller watches for updates to the contacts it is already displaying, but does not add or delete contacts automatically. You might considering listening for adds/deletes and reloading as required.


CareKit's store is a bit of a unique beast. It doesn't behave the way you might expect a typical database to, and that's by design.

The most important thing to understand is that OCKStore is an append only record of immutable values, and that it has the notion of a timeline.

CareKit's store allows you to look backward and forward in time to see exactly what the state of the store was or will be at any point. This is extremely useful for physicians who want to, for example, review a patient's medication history. For contacts, we might want to look back in time find out who a patient's primary care physician was on the date of an important incident.

When you delete a contact, all you're really doing under the hood is setting a date flag that tells CareKit not to return that contact for queries with a start date after "right now". That means that if you try to query for contacts by providing a date earlier in the day, the one you "deleted" will still show up because you're viewing a sliver of time from before the deletion happened.


The way the contact list view controller works today is that it queries for all contacts (in viewDidLoad, IIRC) and then watches for updates to all contacts it finds at that time. I don't believe it is setup to watch for additions and deletions presently -- that's something we hope to handle in a future revision.

After you've deleted a contact, does it still appear if you back out of the contact list view controller and then return again? If so, that might be a bug.

If you want the contact list to update when contacts are added or deleted, you might be able to subscribe to notifications from the sync'd store manager and prompt the contact list view controller to refresh itself when a contact added/deleted notification comes through.

erik-apple avatar Sep 22 '20 15:09 erik-apple

@erik-apple @gavirawson-apple Thank you guys so much for your kind explanation. I just want to say again you guys are doing some amazing work on these libraries, and it's an honor to be able to even ask you guys questions.

So before when I was still on OCKContactListViewController, I would delete contact with the codes I provided in my original post. Even after triggering viewDidLoad(), the contact will not disappear.

When I opened up OCKConctactsListViewController, lines 93-98 shows that this vc is subscribed to [.add, .update] publishers, right? (forgive me, I am still new to combine 😢 )

That's why I thought no matter how I delete the contacts, it would still appear after I've done my deletion because publisher isn't listening to .delete?

And gavirawson, as for the query part (That initializer takes an id rather than a uuid:), I knew that id is a human readable identifier, but obviously I was not able to create multiple task or contact using the same identifier, so I thought to use UUID().uuidString, because I thought it didn't matter what the actual content of the String was as long as it's String, which UUID().uuidString is. So when fetch my Core Data's Contact results and filter and match the selected OCKContact's id with that of core data contact object's id property, I can know which OCKContact to delete in Core Data, OCKStore, and remove it from UI accordingly.

OCKStore really is intriguing and like erik said, calling it a beast is quite the understatement. I'm just having harder time wrapping my head around the whole "timeline" aspect I guess. I understand why that might be a useful way to have this persistence set up for CareKit, and I suppose in that sense I am using CareKit improperly. I love the aspect of setting up tasks for patients to complete, which drew me in at first. But unlike Apple's Health App or even the OCKSampleApp, my vision for using CareKit is more around user's ability to configure freely, as in adding tasks as they see fit, removing ones they don't want to track anymore, updating the tasks they had made already, etc. I can even turn my eyes away from the updating aspect for now, but deleting aspect seems to be quite the headache.

I will make a sample app to showcase what I was trying to accomplish and post the repo, if you guys would take a look at it. I will comment on this thread once the repo is ready, if that is okay with you guys.

chriisong avatar Sep 22 '20 16:09 chriisong

I just wanted to update on what I've done:

I've read through the posts here and went ahead with creating a custom adherence aggregator that filters through all the events that users had created and only aggregate the events that match my predicate.

The result is what I had wanted this whole time! I think just the whole concept of lossless data is so new.

Will I have to eventually worry about the events/contacts/tasks/carePlans that accumulate over time? Will there be a method to "clean out" the system?

ninja edit: off topic but i didn't want to create yet another post, and i didnt see anyone asking the same question. How do i get rid of the completionLabel under OCKGridTaskViewController? It leaves me with EVENTS_REMAINING. I see under OCKGridTaskCell: OCKEventUpdatable that there is an updateWith(event: animated:) method that handles the completedTimeLabel, but I am not sure how to access that method

Thank you again :)

chriisong avatar Sep 23 '20 02:09 chriisong