-
Notifications
You must be signed in to change notification settings - Fork 0
/
HomeViewModel.swift
executable file
·173 lines (146 loc) · 6.47 KB
/
HomeViewModel.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//
// HomeViewModel.swift
// PainMedsBuddy
//
// Created by Jules Moorhouse.
//
import CoreData
import Foundation
extension HomeView {
class ViewModel: NSObject, ObservableObject, NSFetchedResultsControllerDelegate {
private let dosesController: NSFetchedResultsController<Dose>
private let medsController: NSFetchedResultsController<Med>
@Published var doses = [Dose]()
private var meds = [Med]()
var reaffirmedDoses: [Dose] {
if !doses.isEmpty {
return filterReaffirmedDoses(
loadedDoses: dosesController.fetchedObjects ?? [])
}
return []
}
var recentMeds: [Med] {
getRecentMeds(
loadedDoses: dosesController.fetchedObjects ?? [],
loadedMeds: medsController.fetchedObjects ?? []
)
}
var lowMeds: [Med] {
if !meds.isEmpty {
return getLowMeds(
loadedMeds: medsController.fetchedObjects ?? [])
}
return []
}
private let dataController: DataController
init(dataController: DataController) {
self.dataController = dataController
// INFO: Construct a fetch request to show all none elapsed doses
let doseRequest: NSFetchRequest<Dose> = Dose.fetchRequest()
doseRequest.sortDescriptors = [
NSSortDescriptor(keyPath: \Dose.takenDate, ascending: true),
]
dosesController = NSFetchedResultsController(
fetchRequest: doseRequest,
managedObjectContext: dataController.container.viewContext,
sectionNameKeyPath: nil,
cacheName: nil
)
// INFO: Construct a fetch request to show all none hidden meds
let medRequest: NSFetchRequest<Med> = Med.fetchRequest()
medRequest.predicate = !DataController.useHardDelete
? NSPredicate(format: "hidden = false")
: nil
medRequest.sortDescriptors = [
NSSortDescriptor(keyPath: \Med.lastTakenDate, ascending: true),
]
medsController = NSFetchedResultsController(
fetchRequest: medRequest,
managedObjectContext: dataController.container.viewContext,
sectionNameKeyPath: nil,
cacheName: nil
)
super.init()
dosesController.delegate = self
medsController.delegate = self
do {
try dosesController.performFetch()
try medsController.performFetch()
doses = dosesController.fetchedObjects ?? []
meds = medsController.fetchedObjects ?? []
} catch {
print("ERROR: Failed to fetch initial data: \(error)")
}
}
// NOTE: Re-filter core data results, when items are changed
// updated / added, as the init method which uses the
// predicate isn't called.
func filterReaffirmedDoses(loadedDoses: [Dose]) -> [Dose] {
// NOTE: Produce an array of all doses which are
// in the 3 hours soft elapsed date range, these
// are elapsed and not hidden
let availableSoft = loadedDoses.filter {
if let soft = $0.softElapsedDate {
let notPassedSoftElapse = soft >= Date()
let elapsed = $0.elapsed == true
if let med = $0.med {
let result = elapsed && (notPassedSoftElapse && !med.hidden)
return result
}
}
return false
}
// NOTE: Create an array of available soft elapsed doses
// with unique meds
var uniqueAvailableSoft = [Dose]()
for dose in availableSoft {
if !uniqueAvailableSoft.contains(where: {
$0.med?.objectID == dose.med?.objectID
}) {
uniqueAvailableSoft.append(dose)
}
}
// NOTE: Produce an array of all doses currently
// being taken, even those which may have been hidden.
let inProgress = loadedDoses.filter { $0.elapsed == false }
// NOTE: From the availableSoft array remove any doses
// which use the same med as inProgress, aka so the
// user doesn't try and take a dose of an available dose
// which is currently in progress
let noAvailableOverdoses = uniqueAvailableSoft.filter { soft in
!inProgress.contains(where: { $0.med?.objectID == soft.med?.objectID })
}
// NOTE: Add the two doses together
let filtered = noAvailableOverdoses + inProgress
return filtered.sorted(by: \Dose.doseElapsedDateSort)
}
// INFO: Get a unique list of medications that don't have currently active doses.
func getRecentMeds(loadedDoses: [Dose], loadedMeds: [Med]) -> [Med] {
// INFO: Get med doses which are in progress
let currentMeds = reaffirmedDoses.map(\.med)
// INFO: Get in progress doses which aren't hidden
let uniqueDoseMeds = Array(Set(loadedDoses.filter {
$0.med != nil
&& !$0.med!.hidden
&& $0.med!.lastTakenDate != nil }.compactMap(\.med)))
// INFO: Remove current meds from in progress array
let temp = uniqueDoseMeds.filter { !currentMeds.contains($0) }
// INFO: Removed meds filter as this included meds with no doses
let sorted = temp.sortedItems(using: .lastTaken).reversed()
let count = sorted.isEmpty ? 0 : 3
let mapped = sorted.prefix(count).map { $0 }
return mapped
}
func getLowMeds(loadedMeds: [Med]) -> [Med] {
let temp = loadedMeds.filter { $0.medIsRunningLow == true && !$0.hidden }
.sortedItems(using: .remaining)
let count = temp.isEmpty ? 0 : 3
return temp.prefix(count).map { $0 }
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
print("### HomeViewModel - controllerDidChangeContent")
doses = dosesController.fetchedObjects ?? []
meds = medsController.fetchedObjects ?? []
}
}
}