From 1e6e5bba9edc35fdcd3517291ab6c8828624f7db Mon Sep 17 00:00:00 2001 From: Corey Date: Wed, 3 Jul 2024 10:57:25 -0700 Subject: [PATCH] feat: Make more CareKitEssentialView methods public (#8) * feat: Make createOutcomeWithValues public * update readme * update readme * nit * fix spi documentation * add new method * nit * remove swiftlint sandboxing --- .spi.yml | 4 +- CareKitEssentials.xcodeproj/project.pbxproj | 6 +- README.md | 18 ++- .../Cards/Shared/CardViewModel.swift | 2 +- .../Cards/Shared/CareKitEssentialView.swift | 105 ++++++++---------- .../Cards/iOS/LabeledValueTaskView.swift | 6 +- 6 files changed, 72 insertions(+), 69 deletions(-) diff --git a/.spi.yml b/.spi.yml index 427018ec..d8699369 100644 --- a/.spi.yml +++ b/.spi.yml @@ -1,5 +1,5 @@ version: 1 builder: configs: - - documentation_targets: [CareKitUtilities] - swift_version: 5.9 + - documentation_targets: [CareKitEssentials] + swift_version: 6.0 diff --git a/CareKitEssentials.xcodeproj/project.pbxproj b/CareKitEssentials.xcodeproj/project.pbxproj index ecd7a1f6..e744e3f9 100644 --- a/CareKitEssentials.xcodeproj/project.pbxproj +++ b/CareKitEssentials.xcodeproj/project.pbxproj @@ -199,10 +199,10 @@ isa = PBXGroup; children = ( OBJ_26 /* Calendar+Dates.swift */, - 700DA9062C2A66BF00435E2C /* Logger.swift */, 911BDB272A11C491004F8442 /* CGFloat.swift */, OBJ_32 /* CustomLinearCareTaskProgress.swift */, OBJ_27 /* Image.swift */, + 700DA9062C2A66BF00435E2C /* Logger.swift */, OBJ_29 /* OCKAnyEvent.swift */, OBJ_28 /* OCKAnyEvent+CustomStringConvertable.swift */, OBJ_30 /* OCKAnyOutcome.swift */, @@ -489,7 +489,7 @@ ENABLE_NS_ASSERTIONS = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -547,7 +547,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = s; GCC_PREPROCESSOR_DEFINITIONS = ( diff --git a/README.md b/README.md index 2678b760..2a7afa5e 100644 --- a/README.md +++ b/README.md @@ -7,4 +7,20 @@ ![Codecov](https://codecov.io/gh/netreconlab/CareKitEssentials/branches/main/graph/badge.svg) [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/netreconlab/ParseCareKit/#license) -A description of this package. +Provides essential cards, views, models, protocols, and extentions to expedite building [CareKit](https://github.com/carekit-apple/CareKit) based applications. + +## Entensions +A number of public extensions are available to make using CareKit easier. All of the extensions can be found in the [Extensions](https://github.com/netreconlab/CareKitEssentials/tree/main/Sources/CareKitEssentials/Extensions) folder. + +## Usage +You can create SwiftUI views that conform to [CareKitEssentialView](https://github.com/netreconlab/CareKitEssentials/blob/main/Sources/CareKitEssentials/Cards/Shared/CareKitEssentialView.swift) to obtain a number of convenience methods for saving and deleting outcomes. The following views are based on `CareKitEssentialView`: + +### iOS +[SliderLogTaskView](https://github.com/netreconlab/CareKitEssentials/blob/main/Sources/CareKitEssentials/Cards/iOS/SliderLog/SliderLogTaskView.swift) can be used to quickly create a slider view + +image + +### watchOS +[DigitalCrownView](https://github.com/netreconlab/CareKitEssentials/blob/main/Sources/CareKitEssentials/Cards/watchOS/DigitalCrown/DigitalCrownView.swift) can be used to quickly create a view that responds to the crown + +image diff --git a/Sources/CareKitEssentials/Cards/Shared/CardViewModel.swift b/Sources/CareKitEssentials/Cards/Shared/CardViewModel.swift index 21761f08..600600d9 100644 --- a/Sources/CareKitEssentials/Cards/Shared/CardViewModel.swift +++ b/Sources/CareKitEssentials/Cards/Shared/CardViewModel.swift @@ -64,7 +64,7 @@ open class CardViewModel: ObservableObject { public private(set) var detailsInformation: String? var initialValue: OCKOutcomeValue - var action: ((OCKOutcomeValue?) async -> Void)? = nil + var action: ((OCKOutcomeValue?) async -> Void)? /// Create an instance with specified content for an event. The view will update when changes /// occur in the store. diff --git a/Sources/CareKitEssentials/Cards/Shared/CareKitEssentialView.swift b/Sources/CareKitEssentials/Cards/Shared/CareKitEssentialView.swift index bbae9c93..8bec1d52 100644 --- a/Sources/CareKitEssentials/Cards/Shared/CareKitEssentialView.swift +++ b/Sources/CareKitEssentials/Cards/Shared/CareKitEssentialView.swift @@ -27,14 +27,12 @@ public protocol CareKitEssentialView: View { with values: [OCKOutcomeValue]? ) async throws - /// Update an `OCKAnyEvent` with new a `OCKOutcome`. + /// Save a new `OCKAnyOutcome`. /// - Parameters: - /// - event: The event to update. - /// - outcome: A new `OCKOutcome`. - /// - Throws: An error if the outcome values cannot be updated. - func updateEvent( - _ event: OCKAnyEvent, - with outcome: OCKOutcome? + /// - outcome: A new `OCKAnyOutcome`. + /// - Throws: An error if the outcome cannot be updated. + func saveOutcome( + _ outcome: OCKAnyOutcome ) async throws /// Create an `OCKEventQuery` constrained to a set of `taskIDs` on a particular date. @@ -50,43 +48,31 @@ public protocol CareKitEssentialView: View { public extension CareKitEssentialView { + func deleteEventOutcome(_ event: OCKAnyEvent) async throws { + guard let outcome = event.outcome else { + throw CareKitEssentialsError.errorString("The event does not contain an outcome: \(event)") + } + _ = try await careStore.deleteAnyOutcome(outcome) + } + func updateEvent( _ event: OCKAnyEvent, with values: [OCKOutcomeValue]? ) async throws { guard let values = values else { // Attempts to delete outcome if it already exists. - _ = try await self.saveOutcomeValues( - [], - event: event, - store: careStore - ) + try await deleteEventOutcome(event) return } _ = try await self.appendOutcomeValues( values, - event: event, - store: careStore + event: event ) } - func updateEvent( - _ event: OCKAnyEvent, - with outcome: OCKOutcome? + func saveOutcome( + _ outcome: OCKAnyOutcome ) async throws { - guard let outcome = outcome else { - guard let task = event.task as? OCKAnyVersionableTask else { - throw CareKitEssentialsError.errorString("Cannot make outcome for event: \(event)") - } - let outcome = OCKOutcome( - taskUUID: task.uuid, - taskOccurrenceIndex: event.scheduleEvent.occurrence, - values: [] - ) - // Attempts to set the latest outcome values to an empty array. - _ = try await careStore.deleteAnyOutcome(outcome) - return - } _ = try await careStore.addAnyOutcome(outcome) } @@ -98,22 +84,20 @@ public extension CareKitEssentialView { /// Otherwise a new `OCKOutcome` is created with the respective outcome values. func appendOutcomeValues( _ values: [OCKOutcomeValue], - event: OCKAnyEvent, - store: OCKAnyStoreProtocol + event: OCKAnyEvent ) async throws { // Update the outcome with the new value guard var outcome = event.outcome else { - let outcome = try createOutcomeWithValues( + let outcome = createOutcomeWithValues( values, - event: event, - store: store + event: event ) - _ = try await store.addAnyOutcome(outcome) + _ = try await careStore.addAnyOutcome(outcome) return } outcome.values.append(contentsOf: values) - _ = try await store.updateAnyOutcome(outcome) + _ = try await careStore.updateAnyOutcome(outcome) return } @@ -124,35 +108,42 @@ public extension CareKitEssentialView { /// - Note: Setting `values` to an empty array will delete the current `OCKOutcome` if it currently exists. func saveOutcomeValues( _ values: [OCKOutcomeValue], - event: OCKAnyEvent, - store: OCKAnyStoreProtocol + event: OCKAnyEvent ) async throws { // Check if outcome values need to be updated. guard !values.isEmpty else { // If the event has already been completed - guard let oldOutcome = event.outcome else { - return - } - // Delete the outcome, and create a new one. - _ = try await store.deleteAnyOutcome(oldOutcome) + try await deleteEventOutcome(event) return } // If the event has already been completed guard var currentOutcome = event.outcome else { // Create a new outcome with the new values. - let outcome = try createOutcomeWithValues( + let outcome = createOutcomeWithValues( values, - event: event, - store: store + event: event ) - _ = try await store.addAnyOutcome(outcome) + _ = try await careStore.addAnyOutcome(outcome) return } // Update the outcome with the new values. currentOutcome.values = values - _ = try await store.updateAnyOutcome(currentOutcome) + _ = try await careStore.updateAnyOutcome(currentOutcome) + } + + /// Create an outcome for an event. + /// - Parameters: + /// - event: The event the outcome is made for. + func createOutcomeForEvent( + _ event: OCKAnyEvent + ) -> OCKAnyOutcome { + OCKOutcome( + taskUUID: event.task.uuid, + taskOccurrenceIndex: event.scheduleEvent.occurrence, + values: [] + ) } static func eventQuery( @@ -170,19 +161,13 @@ extension CareKitEssentialView { /// Create an outcome for an event with the given outcome values. /// - Parameters: /// - values: The outcome values to attach to the outcome. + /// - event: The event the outcome is made for. func createOutcomeWithValues( _ values: [OCKOutcomeValue], - event: OCKAnyEvent, - store: OCKAnyStoreProtocol - ) throws -> OCKAnyOutcome { - guard let task = event.task as? OCKAnyVersionableTask else { - throw CareKitEssentialsError.errorString("Cannot make outcome for event: \(event)") - } - let outcome = OCKOutcome( - taskUUID: task.uuid, - taskOccurrenceIndex: event.scheduleEvent.occurrence, - values: values - ) + event: OCKAnyEvent + ) -> OCKAnyOutcome { + var outcome = createOutcomeForEvent(event) + outcome.values = values return outcome } diff --git a/Sources/CareKitEssentials/Cards/iOS/LabeledValueTaskView.swift b/Sources/CareKitEssentials/Cards/iOS/LabeledValueTaskView.swift index dad9131a..3eb14578 100644 --- a/Sources/CareKitEssentials/Cards/iOS/LabeledValueTaskView.swift +++ b/Sources/CareKitEssentials/Cards/iOS/LabeledValueTaskView.swift @@ -22,8 +22,10 @@ public extension LabeledValueTaskView where Header == InformationHeaderView { /// - Parameters: /// - event: The data that appears in the view. /// - numberFormatter: An object that formats the progress and target values. - init(event: CareStoreFetchedResult, - numberFormatter: NumberFormatter? = nil) { + init( + event: CareStoreFetchedResult, + numberFormatter: NumberFormatter? = nil + ) { let currentEvent = event.result