react-native-healthkit icon indicating copy to clipboard operation
react-native-healthkit copied to clipboard

feat: added saveStateOfMindSample

Open nickmcmillan opened this issue 11 months ago • 3 comments

This adds the ability to save a State of Mind sample. Ref: https://github.com/kingstinct/react-native-healthkit/issues/134

Example usage:

const res = await saveStateOfMindSample({
  date: new Date(),
  kind: HKStateOfMindKind.dailyMood,
  valence: -0.5,
  labels: [HKStateOfMindLabel.stressed, HKStateOfMindLabel.worried],
  associations: [HKStateOfMindAssociation.currentEvents],
  metadata: {
    key: 'value',
  },
})

One challenge I ran into was that on the React Native side the labels and associations are arrays of enums, which is great. But the value of those enums is lost when passing it over the bridge: Swift just receives an array of Int's. So those Int's need to be converted back into enums on the Swift side.

I've solved this by writing extensions for Label and Association (see Helpers.swift). It feels a bit like extra work having to re-declare everything. I'm new to Swift so wondering if there's a more efficient way to do this?

Summary by CodeRabbit

  • New Features

    • Introduced enhanced support for tracking mental health data by enabling users to save and query state of mind samples on iOS 18 and later.
    • Added human-friendly representations for mood statuses and integrated the functionality into our React Native module, ensuring smooth asynchronous operations.
  • Tests

    • Updated testing utilities to simulate state of mind sample operations, improving overall reliability.

nickmcmillan avatar Feb 14 '25 01:02 nickmcmillan

Walkthrough

The changes expand HealthKit functionality for iOS 18+ by introducing new extensions on the HKStateOfMind types to support human-readable descriptions and safe integer-to-enum conversions. A new method for saving state of mind samples is added to the React Native HealthKit modules in both Objective-C and Swift, with proper checks for iOS version compatibility. Additionally, the corresponding TypeScript interfaces, exports, and test mocks are updated, along with a new utility function to invoke the native functionality.

Changes

Files Change Summary
ios/Helpers.swift Added extensions for HKStateOfMind.Kind, HKStateOfMind.Label, and HKStateOfMind.Association implementing CaseIterable and CustomStringConvertible, plus static methods for safe integer-to-state conversions under iOS 18+ conditions.
ios/ReactNativeHealthkit.m
ios/ReactNativeHealthkit.swift
Introduced new method saveStateOfMindSample to save state of mind samples, incorporating HealthKit availability and iOS version checks.
src/index.ios.tsx
src/index.native.tsx
Added saveStateOfMindSample to module exports: on iOS invoking the proper function and on native returning a resolved false promise via the UnavailableFn utility.
src/native-types.ts
src/test-setup.ts
Updated the ReactNativeHealthkitTypeNative interface to add saveStateOfMindSample and modified the metadata property in HKStateOfMindSampleRaw; also added a mock implementation for saveStateOfMindSample in the test setup.
src/utils/saveStateOfMindSample.ts Created a new asynchronous utility function that accepts a modified sample options object, converts the provided date to an ISO string, and calls Native.saveStateOfMindSample with default values for optional properties.

Sequence Diagram(s)

sequenceDiagram
  participant JS as JavaScript Client
  participant Util as SaveStateOfMind Utility
  participant RNHK as ReactNativeHealthkit (iOS)
  participant HK as HKHealthStore

  JS->>Util: Call saveStateOfMindSample(options)
  Util->>RNHK: Invoke saveStateOfMindSample(date, kind, ... )
  RNHK->>RNHK: Check iOS version & HealthKit availability
  RNHK->>HK: Create and Save HKStateOfMind Sample
  HK-->>RNHK: Success/Error Callback
  RNHK-->>Util: Return result (resolve/reject)
  Util-->>JS: Complete promise resolution

Poem

I’m just a rabbit in this digital glen,
Hopping through code changes time and again.
New state-of-mind functions now in sight,
With HealthKit’s magic shining bright.
I celebrate with hops and a joyful grin—
CodeRabbit’s changes make my day begin! 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI Review profile: ASSERTIVE Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c29a61a64b9449edc3b4e7edce2df4731d9f702 and 8230c4342449ae476498d760b4121b231cb0c789.

📒 Files selected for processing (7)
  • ios/ReactNativeHealthkit.m (1 hunks)
  • ios/ReactNativeHealthkit.swift (1 hunks)
  • src/index.ios.tsx (3 hunks)
  • src/index.native.tsx (3 hunks)
  • src/native-types.ts (2 hunks)
  • src/test-setup.ts (1 hunks)
  • src/utils/saveStateOfMindSample.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
src/utils/saveStateOfMindSample.ts (1)
Learnt from: nickmcmillan
PR: kingstinct/react-native-healthkit#136
File: src/utils/saveStateOfMindSample.ts:9-36
Timestamp: 2025-02-17T01:27:43.286Z
Learning: In the react-native-healthkit library, methods follow a pattern of letting native errors surface through the returned promise without additional error handling layers.
ios/ReactNativeHealthkit.swift (2)
Learnt from: nickmcmillan
PR: kingstinct/react-native-healthkit#136
File: ios/ReactNativeHealthkit.swift:686-692
Timestamp: 2025-02-17T01:39:41.314Z
Learning: In react-native-healthkit, version-specific features should reject with clear error messages when the required iOS version is not available, following the library's established pattern, rather than providing fallbacks or no-ops.
Learnt from: nickmcmillan
PR: kingstinct/react-native-healthkit#136
File: ios/ReactNativeHealthkit.swift:679-685
Timestamp: 2025-02-17T01:44:31.212Z
Learning: In react-native-healthkit, authorization checks are handled by the consumer before calling save functions, not within the save functions themselves. This is a deliberate architectural decision consistent across the library.
🧬 Code Graph Analysis (3)
src/index.native.tsx (2)
src/index.ios.tsx (1)
  • saveStateOfMindSample (272-272)
ios/ReactNativeHealthkit.swift (1)
  • saveStateOfMindSample (658-702)
src/utils/saveStateOfMindSample.ts (2)
ios/ReactNativeHealthkit.swift (1)
  • saveStateOfMindSample (658-702)
src/native-types.ts (1)
  • HKStateOfMindSampleRaw (2353-2370)
ios/ReactNativeHealthkit.swift (3)
src/index.ios.tsx (1)
  • saveStateOfMindSample (272-272)
src/index.native.tsx (1)
  • saveStateOfMindSample (212-212)
ios/Helpers.swift (3)
  • convertToStateOfMindKind (274-277)
  • convertToStateOfMindLabels (341-347)
  • convertToStateOfMindAssociations (387-392)
🪛 ESLint
src/utils/saveStateOfMindSample.ts

[error] 1-1: Definition for rule '@typescript-eslint/indent' was not found.

(@typescript-eslint/indent)


[error] 1-1: Definition for rule '@typescript-eslint/type-annotation-spacing' was not found.

(@typescript-eslint/type-annotation-spacing)


[error] 1-1: Definition for rule 'functional/immutable-data' was not found.

(functional/immutable-data)


[error] 1-1: Definition for rule 'functional/prefer-readonly-type' was not found.

(functional/prefer-readonly-type)


[error] 1-1: Definition for rule 'functional/prefer-tacit' was not found.

(functional/prefer-tacit)


[error] 1-1: Definition for rule 'unicorn/expiring-todo-comments' was not found.

(unicorn/expiring-todo-comments)


[error] 1-1: Definition for rule 'unicorn/no-abusive-eslint-disable' was not found.

(unicorn/no-abusive-eslint-disable)


[error] 1-1: Definition for rule 'unicorn/prefer-array-find' was not found.

(unicorn/prefer-array-find)


[error] 1-1: Definition for rule 'unicorn/prefer-array-index-of' was not found.

(unicorn/prefer-array-index-of)


[error] 1-1: Definition for rule 'unicorn/prefer-array-some' was not found.

(unicorn/prefer-array-some)


[error] 1-1: Definition for rule 'unicorn/prefer-at' was not found.

(unicorn/prefer-at)


[error] 1-1: Definition for rule 'unicorn/prefer-date-now' was not found.

(unicorn/prefer-date-now)


[error] 1-1: Definition for rule 'unicorn/prefer-includes' was not found.

(unicorn/prefer-includes)


[error] 1-1: Definition for rule 'unicorn/prefer-set-has' was not found.

(unicorn/prefer-set-has)


[error] 1-1: Definition for rule 'unicorn/prefer-ternary' was not found.

(unicorn/prefer-ternary)


[error] 1-1: Definition for rule 'unicorn/no-nested-ternary' was not found.

(unicorn/no-nested-ternary)


[error] 1-1: Definition for rule 'unicorn/consistent-function-scoping' was not found.

(unicorn/consistent-function-scoping)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: eas-ios / eas-build
🔇 Additional comments (8)
src/test-setup.ts (1)

38-38: LGTM! Test mock implementation is correct.

The Jest mock for saveStateOfMindSample follows the established pattern and is properly placed within the mockModule object.

src/index.native.tsx (2)

100-100: LGTM! Proper cross-platform fallback implementation.

The saveStateOfMindSample function correctly uses the UnavailableFn pattern to provide a fallback for non-iOS platforms, returning Promise.resolve(false) which is appropriate for a save operation.


154-154: LGTM! Function properly integrated into exports.

The function is correctly added to both the Healthkit object and named exports, maintaining consistency with other functions in the module.

Also applies to: 212-212

src/index.ios.tsx (1)

39-39: LGTM! Import statement is correct.

The import of saveStateOfMindSample from the utils directory follows the established pattern for other utility functions.

ios/ReactNativeHealthkit.m (1)

91-99: LGTM! Objective-C method declaration is correct.

The RCT_EXTERN_METHOD declaration for saveStateOfMindSample is properly formatted with appropriate parameter types:

  • NSDate for the date
  • NSInteger for the kind (enum value)
  • double for valence
  • NSArray for labels and associations
  • NSDictionary for metadata
  • Promise resolve/reject blocks for async handling

The declaration matches the Swift implementation and follows the established pattern for other external methods in this file.

src/native-types.ts (1)

2275-2282: LGTM! Well-designed method signature with proper type safety.

The new saveStateOfMindSample method follows the established patterns in the codebase:

  • Parameters correctly reference HKStateOfMindSampleRaw properties for type safety
  • Return type Promise<boolean> is consistent with other save methods
  • Method signature covers all necessary properties for creating a state of mind sample
ios/ReactNativeHealthkit.swift (1)

658-702: Excellent implementation following established library patterns.

The saveStateOfMindSample method is well-implemented and addresses previous review feedback:

Valence validation: Properly clamps values between -1.0 and 1.0 ✅ Version checks: Follows library pattern with clear error messages for unsupported iOS/compiler versions
Error handling: Consistent with other save methods in the library ✅ Authorization: Correctly delegates to consumer as per library architecture

The integration with helper functions for enum conversion and the overall structure align perfectly with the existing codebase patterns.

src/utils/saveStateOfMindSample.ts (1)

9-36: Well-crafted TypeScript utility with excellent type safety.

The implementation demonstrates several strong design choices:

Type manipulation: Expertly uses Omit to exclude HealthKit-generated fields while making labels and associations optional ✅ Date handling: Properly converts Date object to ISO string for native bridge compatibility ✅ Sensible defaults: Provides empty arrays and objects for optional parameters ✅ Error handling: Follows library pattern of letting native errors surface through the promise ✅ Documentation: Clear JSDoc with Apple documentation reference

The function signature provides an excellent developer experience by accepting a more convenient input shape while maintaining type safety.

✨ Finishing Touches
  • [ ] 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

coderabbitai[bot] avatar Feb 14 '25 01:02 coderabbitai[bot]

Open in StackBlitz

npm i https://pkg.pr.new/kingstinct/react-native-healthkit/@kingstinct/react-native-healthkit@136

commit: 8230c43

pkg-pr-new[bot] avatar Feb 14 '25 01:02 pkg-pr-new[bot]

Expo preview is ready for review.

There should soon be a new native build available here

With that installed just scan this QR code: QR Preview

github-actions[bot] avatar May 26 '25 17:05 github-actions[bot]

Closed #134

robertherber avatar Jun 16 '25 14:06 robertherber