Skip to content

fix: Add more Hashable conformance - #737

Open
cbaker6 wants to merge 3 commits into
carekit-apple:mainfrom
cbaker6:moreConformance
Open

fix: Add more Hashable conformance#737
cbaker6 wants to merge 3 commits into
carekit-apple:mainfrom
cbaker6:moreConformance

Conversation

@cbaker6

@cbaker6 cbaker6 commented Dec 14, 2025

Copy link
Copy Markdown
Contributor

Close #726

Continuation on #736

@gavirawson-apple gavirawson-apple left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great, Hashable conformance will make handling these data structures much easier. I think we just need to remove the Identifiable conformance before merging. Let's brainstorm the best way to move towards Identifiable conformance without negatively impacting developers.


/// Conforming a type to `OCKAnyTask` allows it to be queried and displayed by CareKit.
public protocol OCKAnyTask: Sendable {
public protocol OCKAnyTask: Identifiable, Sendable {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have long-standing problem ever since the Identifiable was introduced - our id property isn't a stable ID for different versions of the same task, but our uuid is. I think that means we can't conform OCKAnyTask to Identifiable directly, the best you can do right now is wrap OCKAnyTask in an Identifiable type.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I’ll make an update soon.

Removed Identifiable conformance from OCKAnyTask protocol.
Copilot AI review requested due to automatic review settings January 23, 2026 18:20
@cbaker6 cbaker6 changed the title fix: Add more Hashable and Identifiable conformance fix: Add more Hashable conformance Jan 23, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends Hashable support across additional CareKitStore types to improve SwiftUI interoperability (per #726, continuing #736).

Changes:

  • Make OCKEvent generic parameters Hashable and add Hashable conformance to OCKEvent.
  • Tighten store protocol associated-type constraints so Task/Outcome must be Hashable.
  • Add Hashable conformance to OCKHealthKitOutcome.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
CareKitStore/CareKitStore/Structs/OCKEvent.swift Requires Task/Outcome to be Hashable and makes OCKEvent itself Hashable.
CareKitStore/CareKitStore/Protocols/Tasks/OCKTaskStore.swift Updates OCKReadableTaskStore’s Task associated type constraint to Hashable.
CareKitStore/CareKitStore/Protocols/Outcomes/OCKOutcomeStore.swift Updates OCKReadableOutcomeStore’s Outcome associated type constraint to Hashable.
CareKitStore/CareKitStore/HealthKit/OCKHealthKitOutcome.swift Adds Hashable conformance to OCKHealthKitOutcome.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 37 to +40
public struct OCKEvent<
Task: OCKAnyTask & Equatable,
Outcome: OCKAnyOutcome & Equatable
>: Identifiable, Comparable, Sendable {
Task: OCKAnyTask & Hashable,
Outcome: OCKAnyOutcome & Hashable
>: Identifiable, Hashable, Comparable, Sendable {

Copilot AI Jan 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OCKEvent now conforms to Hashable, but the synthesized Hashable/Equatable implementation will include outcome (a mutable stored property). That means mutating outcome changes the hash/equality, which can break Set/Dictionary invariants and can cause surprising behavior when OCKEvent is used as a SwiftUI navigation value. Consider implementing custom ==/hash(into:) based only on stable identity (e.g., task UUID + occurrence) or making outcome immutable.

Copilot uses AI. Check for mistakes.
Comment on lines 37 to 41
public struct OCKEvent<
Task: OCKAnyTask & Equatable,
Outcome: OCKAnyOutcome & Equatable
>: Identifiable, Comparable, Sendable {
Task: OCKAnyTask & Hashable,
Outcome: OCKAnyOutcome & Hashable
>: Identifiable, Hashable, Comparable, Sendable {

Copilot AI Jan 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OCKEvent.id is documented as a “stable identifier”, but it’s currently derived from Hasher.finalize(). Swift’s Hasher is intentionally seeded per-process, so this value is not stable across app launches and can also theoretically collide. For a stable ID (and to better support SwiftUI use-cases), prefer a deterministic string derived from task.uuid and scheduleEvent.occurrence rather than Hasher output.

Copilot uses AI. Check for mistakes.
Comment on lines 35 to 37

associatedtype Task: OCKAnyTask, Equatable
associatedtype Task: OCKAnyTask, Hashable

Copilot AI Jan 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing OCKReadableTaskStore.Task from Equatable to Hashable is a source-breaking change for any external store implementations that previously only made their task Equatable. If the intent is to add SwiftUI support without breaking existing conformers, consider keeping the base protocol requirement as Equatable and introducing a separate/refinement protocol (or conditional extensions) for Hashable-specific APIs.

Copilot uses AI. Check for mistakes.
public protocol OCKReadableOutcomeStore: OCKAnyReadOnlyOutcomeStore {

associatedtype Outcome: OCKAnyOutcome, Equatable, Identifiable
associatedtype Outcome: OCKAnyOutcome, Hashable, Identifiable

Copilot AI Jan 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing OCKReadableOutcomeStore.Outcome from Equatable to Hashable is a source-breaking change for external outcome store implementations that only provided Equatable outcomes. If avoiding breaking changes is important, consider leaving this protocol as-is and adding a new/refinement protocol that requires Hashable (or a where Outcome: Hashable constrained extension) for SwiftUI-facing APIs.

Suggested change
associatedtype Outcome: OCKAnyOutcome, Hashable, Identifiable
associatedtype Outcome: OCKAnyOutcome, Equatable, Identifiable

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update many of the concrete CareKit types to Hashable for SwiftUI

3 participants