carekit-apple / CareKit

CareKit is an open source software framework for creating apps that help people better understand and manage their health.
https://www.researchandcare.org
Other
2.4k stars 443 forks source link

OCKSchedule task error when start date is not Date() #504

Open chriisong opened 3 years ago

chriisong commented 3 years ago

Whenever I add tasks of any kind using schedule of any composition, as long as the start parameter is Date() it adds, fetches, and presents perfectly as expected.

The moment I try to add a task using .weeklyAtTime or basically any other schedule composition that changes the start parameter by adding days or weeks or months, I get the error [CareKit] Error: Failed to fetch: Failed to fetch completion for tasks! Failed to fetch: Unable to find a task with the given id. (I get this error whenever I scroll the pageViewController and a new start of week shows up)

Setting up schedule and adding task to store:

        var schedule: OCKSchedule? = nil
        let id = UUID().uuidString
        var startTime = Calendar.current.date(bySetting: .weekday, value: 1, of: Date())!
        startTime = Calendar.current.date(bySettingHour: 12, minute: 30, second: 0, of: startTime)!
        let interval = DateComponents(weekOfYear: 1)
        let element = OCKScheduleElement(start: startTime, end: nil, interval: interval, text: "", targetValues: [], duration: .hours(1))
        schedule = OCKSchedule(composing: [element])

        var task = OCKTask(id: id, title: "test title", carePlanUUID: nil, schedule: schedule!)
        task.impactsAdherence = true

        self.store.addAnyTask(task)

That is basically how CareKit calls .weeklyAtTime.

My fetch method (this all happens in my custom OCKDailyPageViewController):

        var query = OCKTaskQuery(for: date)
        query.ids = identifiers
        // identifiers is an array of task's id that I save separately onto Core Data entity, which I fetch before fetching for OCKTasks.
        query.excludesTasksWithNoEvents = true

        self.store.fetchAnyTasks(query: query, callbackQueue: .main) { result in
            switch result {
            case .failure(let error): print(error.localizedDescription)
            case .success(let tasks):
                for task in tasks {
                        let card = TestGridTaskViewController(task: task, eventQuery: .init(for: date), storeManager: self.storeManager)
                        listViewController.appendViewController(card, animated: false)
                }

The OCKChartViewControllerDelegate and OCKTaskViewControllerDelegate both show me the error: Failed to fetch: Unable to find a task with the given id.

When I cross-reference the Core Data's fetched objects' ids (which is the same id that I use for when creating the OCKTask; see above let id = UUID().uuidString) to the tasks of the result of self.store.fetchAnyTasks, they match. So I am really confused why I am getting these delegate error logs that it was unable to find a task with that given id.

gavirawson-apple commented 3 years ago

It's possible that the query is being performed on a day when the task doesn't occur. It looks to me like the task has been set up to occur every first day of the week at 12:30pm. If you query for that task on the second day of the week, you won't get any result back. Does that seem like the issue to you? Do you know when you performed the query?

chriisong commented 3 years ago

Thank you for your response @gavirawson-apple

It's possible that the query is being performed on a day when the task doesn't occur. It looks to me like the task has been set up to occur every first day of the week at 12:30pm.

So it just occurred to me that with the way I set up my query (under dailyPageViewController(_ dailyPageViewController: OCKDailyPageViewController, prepare listViewController: OCKListViewController, for date: Date)), if I make the same schedule as I did in my original post and then change my phone's Date to the first day that my weekly schedule begins, then i get no fetch errors, and the tasks appear properly.

The issue right now:

let's say I make a new schedule like so:

    // create daily task that for 12:30PM, starting on October 7
    // self.startDate is 2020-Oct-7
    // self.endDate is 2020-Oct-14
    let schedule = OCKSchedule.dailyAtTime(hour: 12, minutes: 30, start: self.startDate, end: self.endDate, text: ""
    var task = OCKTask(id: UUID().uuidString, title: "Metformin", carePlanUUID: nil, schedule: schedule!)
    task.impactsAdherence = true
    task.instructions = "Take Metformin with water"

    // let store = (UIApplication.shared.delegate as! AppDelegate).coreDataStore
    // let coreDataStore = OCKStore(name: "REDACTED", type: .onDisk)
    self.store.addTask(task, callbackQueue: .main)

then query the task like so (in my custom OCKDailyPageViewController, under dailyPageViewController(_ dailyPageViewController: prepare listViewController: for date:)):

    var query = OCKTaskQuery(for: date)
    // ids is my array of stored identifiers that I use to create the task (UUID().uuidString from above)
    query.ids = ids
    query.excludesTaskWithNoEvents = true

    self.storeManager.store.fetchAnyTasks(query: query, callbackQueue: .main) { result in
    switch result {
    case .failure(let error): print(error.localizedDescription)
    case .success(let tasks):
            for task in tasks {
            let card = OCKGridTaskViewController(task: task, eventQuery: .init(for: date), storeManager: self.storeManager)
            listViewController.appendViewController(card, animated: false)
            }
    }

Expected behaviour:

Actual behaviour:

So to my understanding, querying for tasks using the initializer (for: date) on both OCKTaskQuery and OCKGridTaskViewController's eventQuery only works if the task has happened?

I'm just not properly understanding how this query thing works I guess. Because if I manually change the device's date to the startDate of my task (where startDate == Oct 7), then everything works as expected. It's just that when querying for a task that hasn't happened yet, all these fetch errors occur.

So what is the best practice for adding a task that hasn't happened yet, as in where startDate != Date()?

gavirawson-apple commented 3 years ago

So what is the best practice for adding a task that hasn't happened yet, as in where startDate != Date()?

It looks like your adding the task to the store correctly! Based on the code in the snippet, the task will occur every day from Oct 7th - Oct 14th.

I think the issue here is just around the task query. There are two things to call out:

  1. In the code snippet, that task is being created with a UUID. If that code is called each time the app runs, a new task will be created on every app launch. I would recommend using a human readable string, like "metformin".

  2. The error you're seeing mentions that the requested task ID could not be found. I would guess that in this line:

query.ids = ids

ids may not be the task ID that's intended. To see if that's the issue, try removing that line completely and check if the task is displayed correctly.

chriisong commented 3 years ago

Thanks for your response.

  1. In the code snippet, that task is being created with a UUID. If that code is called each time the app runs, a new task will be created on every app launch. I would recommend using a human readable string, like "metformin".

Basically, this task is created whenever a user adds prescription information. So the only time a task is created is when a user goes to add a new prescription. That is why I opted to use a UUID string for each prescription created so that if a user has more than one prescription that he or she is taking at the same time, the OCKTask isn't being overridden. I find that you cannot have an OCKTask of the same id, which makes sense, but that hinders me from wanting to achieve what I explained just now.

  1. The error you're seeing mentions that the requested task ID could not be found. I would guess that in this line:
query.ids = ids

ids may not be the task ID that's intended. To see if that's the issue, try removing that line completely and check if the task is displayed correctly.

the query.ids is an array of OCKTask's id, which I initialize when creating the task, as in:

var task = OCKTask(id: UUID().uuidString, title: "Metformin", carePlanUUID: nil, schedule: schedule!)

I thought that was the id that I was "querying".

Regardless, I just do not understand the querying part perhaps because like I mentioned before, if I created a task with startDate that is not Date(), the tasks will appear with no errors whatsoever when I perform the query (with the code I provided above) when that future startDate is the current date. I just get the fetching error if startDate is set to some future date that is not the current date.