diff --git a/CareKitEssentials.xcodeproj/project.pbxproj b/CareKitEssentials.xcodeproj/project.pbxproj index 4fd37912..8d154a64 100644 --- a/CareKitEssentials.xcodeproj/project.pbxproj +++ b/CareKitEssentials.xcodeproj/project.pbxproj @@ -54,6 +54,14 @@ 70C422DA2C3B6C7600E6DC51 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 70C422D92C3B6C7600E6DC51 /* Preview Assets.xcassets */; }; 70C422DF2C3B6C8D00E6DC51 /* CareKitEssentials.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "carekitessentials::CareKitEssentials::Product" /* CareKitEssentials.framework */; }; 70C422E02C3B6C8D00E6DC51 /* CareKitEssentials.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = "carekitessentials::CareKitEssentials::Product" /* CareKitEssentials.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 70EBE30C2D19FC0F00517D5E /* EventWithContentViewable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70EBE30B2D19FC0F00517D5E /* EventWithContentViewable.swift */; }; + 70EBE30D2D19FC0F00517D5E /* EventViewable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70EBE30A2D19FC0F00517D5E /* EventViewable.swift */; }; + 70EBE3102D19FC2A00517D5E /* EventQueryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70EBE30F2D19FC2A00517D5E /* EventQueryView.swift */; }; + 70EBE3112D19FC2A00517D5E /* EventQueryContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70EBE30E2D19FC2A00517D5E /* EventQueryContentView.swift */; }; + 70EBE3132D19FF0200517D5E /* SimpleTaskView+CareStoreFetchedViewable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70EBE3122D19FEE600517D5E /* SimpleTaskView+CareStoreFetchedViewable.swift */; }; + 70EBE3152D19FF9700517D5E /* InstructionsTaskView+CareStoreFetchedViewable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70EBE3142D19FF8D00517D5E /* InstructionsTaskView+CareStoreFetchedViewable.swift */; }; + 70EBE3172D19FFE900517D5E /* LabeledValueTaskView+CareStoreFetchedViewable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70EBE3162D19FFE900517D5E /* LabeledValueTaskView+CareStoreFetchedViewable.swift */; }; + 70EBE3192D19FFF700517D5E /* NumericProgressTaskView+CareStoreFetchedViewable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70EBE3182D19FFF700517D5E /* NumericProgressTaskView+CareStoreFetchedViewable.swift */; }; 70FDC6162C387C9F00A32137 /* NSImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70FDC6152C387C9F00A32137 /* NSImage.swift */; }; 911BDB262A11C437004F8442 /* View+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = 911BDB252A11C437004F8442 /* View+Default.swift */; }; 911BDB282A11C491004F8442 /* CGFloat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 911BDB272A11C491004F8442 /* CGFloat.swift */; }; @@ -178,6 +186,14 @@ 70C422D52C3B6C7600E6DC51 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 70C422D72C3B6C7600E6DC51 /* TestHost.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TestHost.entitlements; sourceTree = ""; }; 70C422D92C3B6C7600E6DC51 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 70EBE30A2D19FC0F00517D5E /* EventViewable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventViewable.swift; sourceTree = ""; }; + 70EBE30B2D19FC0F00517D5E /* EventWithContentViewable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventWithContentViewable.swift; sourceTree = ""; }; + 70EBE30E2D19FC2A00517D5E /* EventQueryContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventQueryContentView.swift; sourceTree = ""; }; + 70EBE30F2D19FC2A00517D5E /* EventQueryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventQueryView.swift; sourceTree = ""; }; + 70EBE3122D19FEE600517D5E /* SimpleTaskView+CareStoreFetchedViewable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SimpleTaskView+CareStoreFetchedViewable.swift"; sourceTree = ""; }; + 70EBE3142D19FF8D00517D5E /* InstructionsTaskView+CareStoreFetchedViewable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "InstructionsTaskView+CareStoreFetchedViewable.swift"; sourceTree = ""; }; + 70EBE3162D19FFE900517D5E /* LabeledValueTaskView+CareStoreFetchedViewable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LabeledValueTaskView+CareStoreFetchedViewable.swift"; sourceTree = ""; }; + 70EBE3182D19FFF700517D5E /* NumericProgressTaskView+CareStoreFetchedViewable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NumericProgressTaskView+CareStoreFetchedViewable.swift"; sourceTree = ""; }; 70FDC6152C387C9F00A32137 /* NSImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSImage.swift; sourceTree = ""; }; 911BDB252A11C437004F8442 /* View+Default.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+Default.swift"; sourceTree = ""; }; 911BDB272A11C491004F8442 /* CGFloat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGFloat.swift; sourceTree = ""; }; @@ -289,7 +305,9 @@ isa = PBXGroup; children = ( 91A9E7DF2A19784800F3414D /* LabeledValueTaskView.swift */, + 70EBE3162D19FFE900517D5E /* LabeledValueTaskView+CareStoreFetchedViewable.swift */, 91A9E7DD2A19758300F3414D /* NumericProgressTaskView.swift */, + 70EBE3182D19FFF700517D5E /* NumericProgressTaskView+CareStoreFetchedViewable.swift */, ); path = Extensions; sourceTree = ""; @@ -297,8 +315,10 @@ 70B49A8E2D0A8FB80023E5B3 /* Extensions */ = { isa = PBXGroup; children = ( - 91A9E7E32A197A9300F3414D /* SimpleTaskView.swift */, 91A9E7E12A197A7300F3414D /* InstructionsTaskView.swift */, + 70EBE3142D19FF8D00517D5E /* InstructionsTaskView+CareStoreFetchedViewable.swift */, + 91A9E7E32A197A9300F3414D /* SimpleTaskView.swift */, + 70EBE3122D19FEE600517D5E /* SimpleTaskView+CareStoreFetchedViewable.swift */, ); path = Extensions; sourceTree = ""; @@ -315,13 +335,14 @@ isa = PBXGroup; children = ( OBJ_23 /* CardViewModel.swift */, - 700DA9042C2A609600435E2C /* CareKitEssentialView.swift */, OBJ_11 /* CustomLabelView.swift */, OBJ_13 /* DetailsView.swift */, OBJ_22 /* InformationHeaderView.swift */, 70B49A5F2D06A2520023E5B3 /* SimpleLabelView.swift */, 702FF7672CFAA8B300B26710 /* Chart */, + 70EBE3082D19FB8E00517D5E /* EventViews */, 70B49A8E2D0A8FB80023E5B3 /* Extensions */, + 70EBE3092D19FBE300517D5E /* Protocols */, ); path = Shared; sourceTree = ""; @@ -382,6 +403,25 @@ name = Frameworks; sourceTree = ""; }; + 70EBE3082D19FB8E00517D5E /* EventViews */ = { + isa = PBXGroup; + children = ( + 70EBE30E2D19FC2A00517D5E /* EventQueryContentView.swift */, + 70EBE30F2D19FC2A00517D5E /* EventQueryView.swift */, + ); + path = EventViews; + sourceTree = ""; + }; + 70EBE3092D19FBE300517D5E /* Protocols */ = { + isa = PBXGroup; + children = ( + 700DA9042C2A609600435E2C /* CareKitEssentialView.swift */, + 70EBE30A2D19FC0F00517D5E /* EventViewable.swift */, + 70EBE30B2D19FC0F00517D5E /* EventWithContentViewable.swift */, + ); + path = Protocols; + sourceTree = ""; + }; OBJ_14 /* DigitalCrown */ = { isa = PBXGroup; children = ( @@ -682,6 +722,8 @@ 7026DDFB2D0D3C86002EACAA /* CareStoreFetchedResult+Hashable.swift in Sources */, 70B49A8B2D0A86C80023E5B3 /* CareStoreFetchedResult.swift in Sources */, 4B27AD842C4ACC4E00661121 /* CareTaskProgressStrategy.swift in Sources */, + 70EBE3102D19FC2A00517D5E /* EventQueryView.swift in Sources */, + 70EBE3112D19FC2A00517D5E /* EventQueryContentView.swift in Sources */, 702FF7712CFAA96B00B26710 /* CKEPoint.swift in Sources */, 700DA9052C2A609600435E2C /* CareKitEssentialView.swift in Sources */, 70B49A602D06A2520023E5B3 /* SimpleLabelView.swift in Sources */, @@ -691,6 +733,7 @@ OBJ_740 /* CustomLabelView.swift in Sources */, 7026DDF72D0D2B33002EACAA /* OCKAnyEvent+Equatable.swift in Sources */, OBJ_742 /* DetailsView.swift in Sources */, + 70EBE3172D19FFE900517D5E /* LabeledValueTaskView+CareStoreFetchedViewable.swift in Sources */, 91A9E7E02A19784800F3414D /* LabeledValueTaskView.swift in Sources */, 70B49A7D2D0A53FA0023E5B3 /* OCKAnyEvent+Hashable.swift in Sources */, OBJ_743 /* DigitalCrownView.swift in Sources */, @@ -704,12 +747,16 @@ 70BBCB552A12BDBD00759A9C /* SliderLogButton.swift in Sources */, OBJ_750 /* CardViewModel.swift in Sources */, 702FF76B2CFAA93C00B26710 /* CareEssentialChartView.swift in Sources */, + 70EBE3192D19FFF700517D5E /* NumericProgressTaskView+CareStoreFetchedViewable.swift in Sources */, 702FF7752CFAA97800B26710 /* TemporalTaskProgress.swift in Sources */, + 70EBE3152D19FF9700517D5E /* InstructionsTaskView+CareStoreFetchedViewable.swift in Sources */, 702FF76D2CFAA96100B26710 /* CKEDataSeries.swift in Sources */, 91A9E7E22A197A7300F3414D /* InstructionsTaskView.swift in Sources */, OBJ_752 /* Calendar+Dates.swift in Sources */, 70B49A852D0A56C10023E5B3 /* OCKOutcomeValue+Hashable.swift in Sources */, OBJ_753 /* Image.swift in Sources */, + 70EBE30C2D19FC0F00517D5E /* EventWithContentViewable.swift in Sources */, + 70EBE30D2D19FC0F00517D5E /* EventViewable.swift in Sources */, 70B49A892D0A83E10023E5B3 /* Double.swift in Sources */, 702FF7732CFAA97500B26710 /* TemporalProgress.swift in Sources */, OBJ_754 /* OCKAnyEvent+CustomStringConvertable.swift in Sources */, @@ -724,6 +771,7 @@ 91A9E7DE2A19758300F3414D /* NumericProgressTaskView.swift in Sources */, 70C422CA2C3B681A00E6DC51 /* OCKAnyOutcome+Sequence.swift in Sources */, 911BDB342A130AF9004F8442 /* OCKStore.swift in Sources */, + 70EBE3132D19FF0200517D5E /* SimpleTaskView+CareStoreFetchedViewable.swift in Sources */, OBJ_757 /* OCKBiologicalSex+Hashable.swift in Sources */, 7026DDED2D0CD600002EACAA /* OCKSemanticVersion+Hashable.swift in Sources */, 7026DDEF2D0CD750002EACAA /* OCKNote+Hashable.swift in Sources */, diff --git a/README.md b/README.md index a8875169..3c7607e0 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Provides essential cards, views, models, protocols, and extensions to expedite b 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`: +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 framework adds a number of additional cards that can be found in the [Cards](https://github.com/netreconlab/CareKitEssentials/tree/main/Sources/CareKitEssentials/Cards) folder. The following add views/cards are based on `CareKitEssentialView`: ### Shared [CareEssentialChartView](https://github.com/netreconlab/CareKitEssentials/blob/main/Sources/CareKitEssentials/Cards/Shared/Chart/CareEssentialChartView.swift) to create charts from CareKit data based on SwiftUI Charts. diff --git a/Sources/CareKitEssentials/Cards/Shared/Chart/CareEssentialChartView.swift b/Sources/CareKitEssentials/Cards/Shared/Chart/CareEssentialChartView.swift index 3faff1c0..6784578e 100644 --- a/Sources/CareKitEssentials/Cards/Shared/Chart/CareEssentialChartView.swift +++ b/Sources/CareKitEssentials/Cards/Shared/Chart/CareEssentialChartView.swift @@ -1,6 +1,6 @@ // // CareEssentialChartView.swift -// Assuage +// CareKitEssentials // // Created by Zion Glover on 6/26/24. // Copyright © 2024 NetReconLab. All rights reserved. diff --git a/Sources/CareKitEssentials/Cards/Shared/Chart/Data/CKEDataSeries.swift b/Sources/CareKitEssentials/Cards/Shared/Chart/Data/CKEDataSeries.swift index 35927fde..44b893d3 100644 --- a/Sources/CareKitEssentials/Cards/Shared/Chart/Data/CKEDataSeries.swift +++ b/Sources/CareKitEssentials/Cards/Shared/Chart/Data/CKEDataSeries.swift @@ -1,6 +1,6 @@ // // CKEDataSeries.swift -// Assuage +// CareKitEssentials // // Created by Alyssa Donawa on 6/11/24. // Copyright © 2024 NetReconLab. All rights reserved. diff --git a/Sources/CareKitEssentials/Cards/Shared/Chart/Data/CKEDataSeriesConfiguration.swift b/Sources/CareKitEssentials/Cards/Shared/Chart/Data/CKEDataSeriesConfiguration.swift index deec55bb..17bc1734 100644 --- a/Sources/CareKitEssentials/Cards/Shared/Chart/Data/CKEDataSeriesConfiguration.swift +++ b/Sources/CareKitEssentials/Cards/Shared/Chart/Data/CKEDataSeriesConfiguration.swift @@ -1,6 +1,6 @@ // // CKEDataSeriesConfiguration.swift -// Assuage +// CareKitEssentials // // Created by Corey Baker on 7/27/24. // Copyright © 2024 NetReconLab. All rights reserved. diff --git a/Sources/CareKitEssentials/Cards/Shared/Chart/Data/CKEPoint.swift b/Sources/CareKitEssentials/Cards/Shared/Chart/Data/CKEPoint.swift index 5a6b7989..89b6de7e 100644 --- a/Sources/CareKitEssentials/Cards/Shared/Chart/Data/CKEPoint.swift +++ b/Sources/CareKitEssentials/Cards/Shared/Chart/Data/CKEPoint.swift @@ -1,6 +1,6 @@ // // CKEPoint.swift -// Assuage +// CareKitEssentials // // Created by Corey Baker on 7/31/24. // Copyright © 2024 NetReconLab. All rights reserved. diff --git a/Sources/CareKitEssentials/Cards/Shared/Chart/Progress/TemporalProgress.swift b/Sources/CareKitEssentials/Cards/Shared/Chart/Progress/TemporalProgress.swift index 7887d242..eca57c46 100644 --- a/Sources/CareKitEssentials/Cards/Shared/Chart/Progress/TemporalProgress.swift +++ b/Sources/CareKitEssentials/Cards/Shared/Chart/Progress/TemporalProgress.swift @@ -1,6 +1,6 @@ // // TemporalProgress.swift -// Assuage +// CareKitEssentials // // Created by Corey Baker on 7/31/24. // Copyright © 2024 NetReconLab. All rights reserved. diff --git a/Sources/CareKitEssentials/Cards/Shared/Chart/Progress/TemporalTaskProgress.swift b/Sources/CareKitEssentials/Cards/Shared/Chart/Progress/TemporalTaskProgress.swift index 714f602d..ca1b8a51 100644 --- a/Sources/CareKitEssentials/Cards/Shared/Chart/Progress/TemporalTaskProgress.swift +++ b/Sources/CareKitEssentials/Cards/Shared/Chart/Progress/TemporalTaskProgress.swift @@ -1,6 +1,6 @@ // // TemporalTaskProgress.swift -// Assuage +// CareKitEssentials // // Created by Corey Baker on 7/31/24. // Copyright © 2024 NetReconLab. All rights reserved. diff --git a/Sources/CareKitEssentials/Cards/Shared/EventViews/EventQueryContentView.swift b/Sources/CareKitEssentials/Cards/Shared/EventViews/EventQueryContentView.swift new file mode 100644 index 00000000..a2b1ecd6 --- /dev/null +++ b/Sources/CareKitEssentials/Cards/Shared/EventViews/EventQueryContentView.swift @@ -0,0 +1,74 @@ +// +// EventQueryContentView.swift +// CareKitEssentials +// +// Created by Corey Baker on 12/10/24. +// Copyright © 2024 NetReconLab. All rights reserved. +// + +import SwiftUI +import CareKit +import CareKitStore +import CareKitUI +import os.log + +/// A view that wraps any view that is `EventWithContentViewable` and provides +/// the respective view with an up-to-date latest event matching the +/// specified event query. +/// - note: This view is useful to wrap around SwiftUI views that will be shown in +/// UIKit view controllers. +/// - important: This view requires `OCKAnyEvent` to conform to `Hashable` +/// and `Equatable` to update view properly. +public struct EventQueryContentView: View { + @Environment(\.careStore) private var store + @CareStoreFetchRequest(query: defaultQuery) var events + + var query: OCKEventQuery + @ViewBuilder let content: () -> CareView.Content + + public var body: some View { + CareView( + event: event.result, + store: store, + content: content + ) + .onAppear { + events.query = query + } + } + + private static var defaultQuery: OCKEventQuery { + var query = OCKEventQuery(for: Date()) + query.taskIDs = [""] + return query + } + + private var event: CareStoreFetchedResult { + guard let latestEvent = events.latest.last else { + // Create empty result to be fill space in UIKit + let taskID = query.taskIDs.first ?? "No taskID supplied in query" + let emptyEvent = OCKAnyEvent.createDummyEvent( + withTaskID: "Task id \"\(taskID)\" not found" + ) + let emptyResult = CareStoreFetchedResult( + id: UUID().uuidString, + result: emptyEvent, + store: store + ) + return emptyResult + } + return latestEvent + } + + /// Create an instance of this view. + /// - Parameters: + /// - query: A query that limits which events will be returned when fetching. + /// - content: Additonal view content to be displayed. + public init( + query: OCKEventQuery, + content: @escaping () -> CareView.Content + ) { + self.query = query + self.content = content + } +} diff --git a/Sources/CareKitEssentials/Cards/Shared/EventViews/EventQueryView.swift b/Sources/CareKitEssentials/Cards/Shared/EventViews/EventQueryView.swift new file mode 100644 index 00000000..0b631fab --- /dev/null +++ b/Sources/CareKitEssentials/Cards/Shared/EventViews/EventQueryView.swift @@ -0,0 +1,94 @@ +// +// EventQueryView.swift +// CareKitEssentials +// +// Created by Corey Baker on 12/10/24. +// Copyright © 2024 NetReconLab. All rights reserved. +// + +import CareKit +import CareKitStore +import CareKitUI +import SwiftUI + +/// A view that wraps any view that is `EventViewable` and provides +/// the respective view with an up-to-date latest event matching the +/// specified event query. +/// - note: This view is useful to wrap around SwiftUI views that will be shown in +/// UIKit view controllers. +/// - important: This view requires `OCKAnyEvent` to conform to `Hashable` +/// and `Equatable` to update view properly. +public struct EventQueryView: View { + @Environment(\.careStore) private var store + @CareStoreFetchRequest(query: defaultQuery) var events + + var query: OCKEventQuery + + public var body: some View { + CareView( + event: event, + store: store + ) + .onAppear { + events.query = query + } + } + + private static var defaultQuery: OCKEventQuery { + var query = OCKEventQuery(for: Date()) + query.taskIDs = [""] + return query + } + + private var event: OCKAnyEvent { + guard let latestEvent = events.latest.last?.result else { + // Create empty result to be fill space in UIKit + let taskID = query.taskIDs.first ?? "No taskID supplied in query" + let emptyEvent = OCKAnyEvent.createDummyEvent( + withTaskID: "Task id \"\(taskID)\" not found" + ) + return emptyEvent + } + return latestEvent + } + + /// Create an instance of this view. + /// - Parameters: + /// - query: A query that limits which events will be returned when fetching. + public init(query: OCKEventQuery) { + self.query = query + } +} + +#Preview { + var query: OCKEventQuery { + var query = OCKEventQuery(for: Date()) + query.taskIDs = [TaskID.doxylamine] + + return query + } + + VStack { + EventQueryView( + query: query + ) + Divider() + EventQueryView( + query: query + ) + #if !os(watchOS) + Divider() + EventQueryView( + query: query + ) + Divider() + EventQueryView( + query: query + ) + #endif + } + .environment(\.careStore, Utility.createPreviewStore()) + .accentColor(.red) + .careKitStyle(OCKStyle()) + .padding() +} diff --git a/Sources/CareKitEssentials/Cards/Shared/Extensions/InstructionsTaskView+CareStoreFetchedViewable.swift b/Sources/CareKitEssentials/Cards/Shared/Extensions/InstructionsTaskView+CareStoreFetchedViewable.swift new file mode 100644 index 00000000..8d894727 --- /dev/null +++ b/Sources/CareKitEssentials/Cards/Shared/Extensions/InstructionsTaskView+CareStoreFetchedViewable.swift @@ -0,0 +1,29 @@ +// +// InstructionsTaskView+CareStoreFetchedViewable.swift +// CareKitEssentials +// +// Created by Corey Baker on 12/23/24. +// Copyright © 2024 Network Reconnaissance Lab. All rights reserved. +// + +import CareKit +import CareKitStore +import CareKitUI +import Foundation +import SwiftUI +import os.log + +extension InstructionsTaskView: EventViewable where Header == InformationHeaderView { + public init?( + event: OCKAnyEvent, + store: OCKAnyStoreProtocol + ) { + self.init( + event: event, + store: store, + onError: { error in + Logger.instructionsTaskView.error("\(error)") + } + ) + } +} diff --git a/Sources/CareKitEssentials/Cards/Shared/Extensions/SimpleTaskView+CareStoreFetchedViewable.swift b/Sources/CareKitEssentials/Cards/Shared/Extensions/SimpleTaskView+CareStoreFetchedViewable.swift new file mode 100644 index 00000000..727a55f0 --- /dev/null +++ b/Sources/CareKitEssentials/Cards/Shared/Extensions/SimpleTaskView+CareStoreFetchedViewable.swift @@ -0,0 +1,29 @@ +// +// SimpleTaskView+CareStoreFetchedViewable.swift +// CareKitEssentials +// +// Created by Corey Baker on 12/23/24. +// Copyright © 2024 Network Reconnaissance Lab. All rights reserved. +// + +import CareKit +import CareKitStore +import CareKitUI +import Foundation +import SwiftUI +import os.log + +extension SimpleTaskView: EventViewable where Header == InformationHeaderView { + public init?( + event: OCKAnyEvent, + store: OCKAnyStoreProtocol + ) { + self.init( + event: event, + store: store, + onError: { error in + Logger.simpleTaskView.error("\(error)") + } + ) + } +} diff --git a/Sources/CareKitEssentials/Cards/Shared/CareKitEssentialView.swift b/Sources/CareKitEssentials/Cards/Shared/Protocols/CareKitEssentialView.swift similarity index 100% rename from Sources/CareKitEssentials/Cards/Shared/CareKitEssentialView.swift rename to Sources/CareKitEssentials/Cards/Shared/Protocols/CareKitEssentialView.swift diff --git a/Sources/CareKitEssentials/Cards/Shared/Protocols/EventViewable.swift b/Sources/CareKitEssentials/Cards/Shared/Protocols/EventViewable.swift new file mode 100644 index 00000000..5048627b --- /dev/null +++ b/Sources/CareKitEssentials/Cards/Shared/Protocols/EventViewable.swift @@ -0,0 +1,24 @@ +// +// EventViewable.swift +// CareKitEssentials +// +// Created by Corey Baker on 12/10/24. +// Copyright © 2024 NetReconLab. All rights reserved. +// + +import CareKit +import CareKitStore +import SwiftUI + +/// Conforming to this protocol ensures your view +/// consists of the proper initializers to view events. +public protocol EventViewable: View { + /// Create an instance of this view. + /// - Parameters: + /// - event: An individual event that contains properties to should be shown in a view. + /// - store: The store in which updates to the respective event should persist. + init?( + event: OCKAnyEvent, + store: any OCKAnyStoreProtocol + ) +} diff --git a/Sources/CareKitEssentials/Cards/Shared/Protocols/EventWithContentViewable.swift b/Sources/CareKitEssentials/Cards/Shared/Protocols/EventWithContentViewable.swift new file mode 100644 index 00000000..ae83481a --- /dev/null +++ b/Sources/CareKitEssentials/Cards/Shared/Protocols/EventWithContentViewable.swift @@ -0,0 +1,28 @@ +// +// EventWithContentViewable.swift +// CareKitEssentials +// +// Created by Corey Baker on 12/13/24. +// Copyright © 2024 NetReconLab. All rights reserved. +// + +import CareKit +import CareKitStore +import SwiftUI + +/// Conforming to this protocol ensures your view +/// consists of the proper initializers to view events. +public protocol EventWithContentViewable: View { + associatedtype Content: View + + /// Create an instance of this view. + /// - Parameters: + /// - event: An individual event that contains properties to should be shown in a view. + /// - store: The store in which updates to the respective event should persist. + /// - content: Additional content to be shown in the view. + init?( + event: OCKAnyEvent, + store: any OCKAnyStoreProtocol, + content: @escaping () -> Content + ) +} diff --git a/Sources/CareKitEssentials/Cards/Shared/SimpleLabelView.swift b/Sources/CareKitEssentials/Cards/Shared/SimpleLabelView.swift index c75572a8..75a7571b 100644 --- a/Sources/CareKitEssentials/Cards/Shared/SimpleLabelView.swift +++ b/Sources/CareKitEssentials/Cards/Shared/SimpleLabelView.swift @@ -1,6 +1,6 @@ // // SimpleLabelView.swift -// Assuage +// CareKitEssentials // // Created by Alyssa Donawa on 9/20/22. // Copyright © 2022 NetReconLab. All rights reserved. diff --git a/Sources/CareKitEssentials/Cards/iOS/Extensions/LabeledValueTaskView+CareStoreFetchedViewable.swift b/Sources/CareKitEssentials/Cards/iOS/Extensions/LabeledValueTaskView+CareStoreFetchedViewable.swift new file mode 100644 index 00000000..4d83131b --- /dev/null +++ b/Sources/CareKitEssentials/Cards/iOS/Extensions/LabeledValueTaskView+CareStoreFetchedViewable.swift @@ -0,0 +1,27 @@ +// +// LabeledValueTaskView+CareStoreFetchedViewable.swift +// CareKitEssentials +// +// Created by Corey Baker on 12/10/24. +// Copyright © 2024 NetReconLab. All rights reserved. +// + +#if !os(watchOS) + +import CareKit +import CareKitStore +import CareKitUI + +extension LabeledValueTaskView: EventViewable where Header == InformationHeaderView { + public init?( + event: OCKAnyEvent, + store: OCKAnyStoreProtocol + ) { + self.init( + event: event, + numberFormatter: nil + ) + } +} + +#endif diff --git a/Sources/CareKitEssentials/Cards/iOS/Extensions/NumericProgressTaskView+CareStoreFetchedViewable.swift b/Sources/CareKitEssentials/Cards/iOS/Extensions/NumericProgressTaskView+CareStoreFetchedViewable.swift new file mode 100644 index 00000000..2496eae8 --- /dev/null +++ b/Sources/CareKitEssentials/Cards/iOS/Extensions/NumericProgressTaskView+CareStoreFetchedViewable.swift @@ -0,0 +1,27 @@ +// +// NumericProgressTaskView+CareStoreFetchedViewable.swift +// CareKitEssentials +// +// Created by Corey Baker on 12/10/24. +// Copyright © 2024 NetReconLab. All rights reserved. +// + +#if !os(watchOS) + +import CareKit +import CareKitStore +import CareKitUI + +extension NumericProgressTaskView: EventViewable where Header == InformationHeaderView { + public init?( + event: OCKAnyEvent, + store: OCKAnyStoreProtocol + ) { + self.init( + event: event, + numberFormatter: nil + ) + } +} + +#endif diff --git a/Sources/CareKitEssentials/Extensions/Logger.swift b/Sources/CareKitEssentials/Extensions/Logger.swift index 86f4eecf..f40dd55c 100644 --- a/Sources/CareKitEssentials/Extensions/Logger.swift +++ b/Sources/CareKitEssentials/Extensions/Logger.swift @@ -14,4 +14,6 @@ extension Logger { static let essentialView = Logger(subsystem: subsystem, category: "CareKitEssentialView") static let essentialChartView = Logger(subsystem: subsystem, category: "CareKitEssentialChartView") static let careKitAnyEventStore = Logger(subsystem: subsystem, category: "CareKitAnyEventStore") + static let instructionsTaskView = Logger(subsystem: subsystem, category: "InstructionsTaskView") + static let simpleTaskView = Logger(subsystem: subsystem, category: "SimpleTaskView") } diff --git a/Sources/CareKitEssentials/Extensions/OCKAnyEvent.swift b/Sources/CareKitEssentials/Extensions/OCKAnyEvent.swift index 68e11928..65eb6bf6 100644 --- a/Sources/CareKitEssentials/Extensions/OCKAnyEvent.swift +++ b/Sources/CareKitEssentials/Extensions/OCKAnyEvent.swift @@ -173,6 +173,39 @@ public extension OCKAnyEvent { } extension OCKAnyEvent { + static func createDummyEvent(withTaskID taskID: String) -> OCKAnyEvent { + + let element = OCKScheduleElement( + start: Date(), + end: nil, + interval: .init(day: 1) + ) + let scheduleEvent = OCKScheduleEvent( + start: .yesterday, + end: .now, + element: element, + occurrence: 1 + ) + let task = OCKTask( + id: taskID, + title: nil, + carePlanUUID: nil, + schedule: .dailyAtTime( + hour: 0, + minutes: 0, + start: .now, + end: nil, + text: nil + ) + ) + let event = OCKAnyEvent( + task: task, + outcome: nil, + scheduleEvent: scheduleEvent + ) + return event + } + func toggleBooleanOutcome(store: T) async throws -> OCKAnyOutcome { try await store.toggleBooleanOutcome(for: self) }