From b8293b367417358a79b768c32483d833b78ebe89 Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 12 Apr 2023 14:52:01 +0200 Subject: [PATCH] Calendar view for activity --- LeCountdown.xcodeproj/project.pbxproj | 20 ++- LeCountdown/Conductor.swift | 22 ++- LeCountdown/LeCountdownApp.swift | 9 ++ LeCountdown/Model/CoreDataRequests.swift | 38 +++++ .../Model/NSManagedContext+Extensions.swift | 19 ++- LeCountdown/Model/Persistence.swift | 14 +- LeCountdown/Stats/Filter.swift | 4 +- LeCountdown/Utils/Date+Extensions.swift | 14 +- LeCountdown/Views/Reusable/CalendarView.swift | 132 ++++++++++++++++++ LeCountdown/Views/Stats/ActivitiesView.swift | 17 ++- .../Views/Stats/ActivityCalendarView.swift | 111 +++++++++++++++ LeCountdown/Views/Stats/ActivityView.swift | 36 ++--- 12 files changed, 394 insertions(+), 42 deletions(-) create mode 100644 LeCountdown/Views/Reusable/CalendarView.swift create mode 100644 LeCountdown/Views/Stats/ActivityCalendarView.swift diff --git a/LeCountdown.xcodeproj/project.pbxproj b/LeCountdown.xcodeproj/project.pbxproj index 0b98e76..2a26ca8 100644 --- a/LeCountdown.xcodeproj/project.pbxproj +++ b/LeCountdown.xcodeproj/project.pbxproj @@ -39,6 +39,9 @@ C415D3FB29C37A460037B215 /* QP01 0096 Wetland lake early morning.wav in Resources */ = {isa = PBXBuildFile; fileRef = C415D3FA29C37A460037B215 /* QP01 0096 Wetland lake early morning.wav */; }; C415D3FD29C37AA40037B215 /* QP01 0118 Riparian Zone thrush.wav in Resources */ = {isa = PBXBuildFile; fileRef = C415D3FC29C37AA40037B215 /* QP01 0118 Riparian Zone thrush.wav */; }; C42E96FB29E59E72005B1B8C /* BackgroundBlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E96FA29E59E72005B1B8C /* BackgroundBlurView.swift */; }; + C42E96FD29E5B06D005B1B8C /* ActivityCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E96FC29E5B06D005B1B8C /* ActivityCalendarView.swift */; }; + C42E96FE29E5B5CD005B1B8C /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B6429A3C37D00CB4FBA /* Filter.swift */; }; + C42E970229E6B32B005B1B8C /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E970129E6B32B005B1B8C /* CalendarView.swift */; }; C438C7C12980228B00BF3EF9 /* CountdownScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7C02980228B00BF3EF9 /* CountdownScheduler.swift */; }; C438C7C5298024E900BF3EF9 /* NSManagedContext+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7C4298024E900BF3EF9 /* NSManagedContext+Extensions.swift */; }; C438C7C929803CA000BF3EF9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7C829803CA000BF3EF9 /* AppDelegate.swift */; }; @@ -363,6 +366,8 @@ C415D3FC29C37AA40037B215 /* QP01 0118 Riparian Zone thrush.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = "QP01 0118 Riparian Zone thrush.wav"; sourceTree = ""; }; C418A14F298428CB00C22230 /* LeCountdown.0.1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.1.xcdatamodel; sourceTree = ""; }; C42E96FA29E59E72005B1B8C /* BackgroundBlurView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundBlurView.swift; sourceTree = ""; }; + C42E96FC29E5B06D005B1B8C /* ActivityCalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityCalendarView.swift; sourceTree = ""; }; + C42E970129E6B32B005B1B8C /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = ""; }; C438C7C02980228B00BF3EF9 /* CountdownScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CountdownScheduler.swift; sourceTree = ""; }; C438C7C4298024E900BF3EF9 /* NSManagedContext+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedContext+Extensions.swift"; sourceTree = ""; }; C438C7C829803CA000BF3EF9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -663,6 +668,13 @@ path = Nature; sourceTree = ""; }; + C42E970329E6DDF1005B1B8C /* Utils */ = { + isa = PBXGroup; + children = ( + ); + path = Utils; + sourceTree = ""; + }; C438C7CF2981216200BF3EF9 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -751,6 +763,7 @@ C438C80B2981DE2E00BF3EF9 /* Views */ = { isa = PBXGroup; children = ( + C42E970329E6DDF1005B1B8C /* Utils */, C4F8B1D3298BF686005C86A5 /* Reusable */, C4F8B1BA298AC83F005C86A5 /* Alarm */, C4F8B1B9298AC830005C86A5 /* Countdown */, @@ -820,9 +833,10 @@ isa = PBXGroup; children = ( C438C80E29828B8600BF3EF9 /* ActivitiesView.swift */, + C42E96FC29E5B06D005B1B8C /* ActivityCalendarView.swift */, C4BA2B6029A3C02400CB4FBA /* ActivityView.swift */, - C4BA2B42299FCB2B00CB4FBA /* RecordsView.swift */, C4BA2B3D299FC86800CB4FBA /* GraphsView.swift */, + C4BA2B42299FCB2B00CB4FBA /* RecordsView.swift */, ); path = Stats; sourceTree = ""; @@ -919,6 +933,7 @@ isa = PBXGroup; children = ( C42E96FA29E59E72005B1B8C /* BackgroundBlurView.swift */, + C42E970129E6B32B005B1B8C /* CalendarView.swift */, C498E5A4299152B400E90DE0 /* GreenCheckmarkView.swift */, C4742B5A298414B000D5D950 /* ImageSelectionView.swift */, C415D3E129C0C0C20037B215 /* MailView.swift */, @@ -1247,6 +1262,7 @@ C4F8B1A7298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataClass.swift in Sources */, C438C7C929803CA000BF3EF9 /* AppDelegate.swift in Sources */, C4556F6F29E40BED00DEB40B /* FileUtils.swift in Sources */, + C42E96FD29E5B06D005B1B8C /* ActivityCalendarView.swift in Sources */, C4BA2B2D299E2DEE00CB4FBA /* Preferences.swift in Sources */, C4F8B185298AC234005C86A5 /* AbstractTimer+CoreDataProperties.swift in Sources */, C4F8B169298AA236005C86A5 /* StopwatchFormView.swift in Sources */, @@ -1262,6 +1278,7 @@ C4F8B17D298AC234005C86A5 /* Record+CoreDataProperties.swift in Sources */, C4F8B184298AC234005C86A5 /* AbstractTimer+CoreDataClass.swift in Sources */, C4F8B17A298AC234005C86A5 /* Countdown+CoreDataClass.swift in Sources */, + C42E970229E6B32B005B1B8C /* CalendarView.swift in Sources */, C4BA2B0F299BE61E00CB4FBA /* Interval+CoreDataClass.swift in Sources */, C4F8B164298A9A92005C86A5 /* AlarmFormView.swift in Sources */, C40FDB622992985C0042A390 /* TextToSpeechRecorder.swift in Sources */, @@ -1391,6 +1408,7 @@ C438C802298132B900BF3EF9 /* LeCountdown.xcdatamodeld in Sources */, C4F8B19B298AC288005C86A5 /* Stopwatch+CoreDataClass.swift in Sources */, C4F8B1A0298AC288005C86A5 /* Activity+CoreDataProperties.swift in Sources */, + C42E96FE29E5B5CD005B1B8C /* Filter.swift in Sources */, C438C81A2982BFF100BF3EF9 /* NSManagedContext+Extensions.swift in Sources */, C4F8B19D298AC288005C86A5 /* AbstractTimer+CoreDataClass.swift in Sources */, C4BA2B3C299F838000CB4FBA /* Model+SharedExtensions.swift in Sources */, diff --git a/LeCountdown/Conductor.swift b/LeCountdown/Conductor.swift index 19c90c3..c8c9290 100644 --- a/LeCountdown/Conductor.swift +++ b/LeCountdown/Conductor.swift @@ -137,8 +137,9 @@ class Conductor: ObservableObject { DispatchQueue.main.async { do { - let date = Date(timeIntervalSinceNow: countdown.duration) - FileLogger.log("schedule countdown \(countdown.stringId) at \(date)") + let start = Date() + let end = start.addingTimeInterval(countdown.duration) + FileLogger.log("schedule countdown \(countdown.stringId) at \(end)") self.removeLiveTimer(id: countdown.stringId) @@ -152,15 +153,15 @@ class Conductor: ObservableObject { try soundPlayer.start(in: countdown.duration, repeatCount: Int(countdown.repeatCount)) - let dateInterval = DateInterval(start: Date(), end: date) + let dateInterval = DateInterval(start: start, end: end) self.currentCountdowns[countdown.stringId] = dateInterval if Preferences.playConfirmationSound { self._playConfirmationSound(timer: countdown) } - self._launchLiveActivity(timer: countdown, date: date) + self._launchLiveActivity(timer: countdown, date: end) - handler(.success(date)) + handler(.success(end)) } catch { FileLogger.log("start error : \(error.localizedDescription)") Logger.error(error) @@ -186,9 +187,11 @@ class Conductor: ObservableObject { fileprivate func _endCountdown(countdownId: String, cancel: Bool) { DispatchQueue.main.async { + #if DEBUG if !cancel { self._recordActivity(countdownId: countdownId) } + #endif self.currentCountdowns.removeValue(forKey: countdownId) } } @@ -197,11 +200,7 @@ class Conductor: ObservableObject { func startStopwatch(_ stopwatch: Stopwatch) { DispatchQueue.main.async { - let lsw = LiveStopWatch(start: Date()) - -// if let liveTimer = liveTimers.first(where: { $0.id == stopwatch.stringId }) { -// liveTimer. -// } + let lsw: LiveStopWatch = LiveStopWatch(start: Date()) self.currentStopwatches[stopwatch.stringId] = lsw @@ -211,9 +210,6 @@ class Conductor: ObservableObject { self._endLiveActivity(timerId: stopwatch.stringId) self._launchLiveActivity(timer: stopwatch, date: lsw.start) - -// self._createTimerIntent(stopwatch) - } } diff --git a/LeCountdown/LeCountdownApp.swift b/LeCountdown/LeCountdownApp.swift index 9367565..9f5bfec 100644 --- a/LeCountdown/LeCountdownApp.swift +++ b/LeCountdown/LeCountdownApp.swift @@ -27,6 +27,15 @@ struct LeCountdownApp: App { Logger.log("path = \(Bundle.main.bundlePath)") + do { + let records = try persistenceController.container.viewContext.fetch(Record.fetchRequest()) + for record in records { + Logger.log("duration = \(record.duration)") + } + } catch { + + } + self._registerBackgroundRefreshes() // if !ProcessInfo.processInfo.isiOSAppOnMac { diff --git a/LeCountdown/Model/CoreDataRequests.swift b/LeCountdown/Model/CoreDataRequests.swift index 8c7d6fa..384754b 100644 --- a/LeCountdown/Model/CoreDataRequests.swift +++ b/LeCountdown/Model/CoreDataRequests.swift @@ -58,4 +58,42 @@ class CoreDataRequests { try context.save() } + static func months(context: NSManagedObjectContext, activity: Activity) throws -> [Month] { + + let predicate: NSPredicate = NSPredicate(format: "activity = %@", activity) + let distinct = try context.distinct(entityName: "Record", attributes: ["year", "month"], predicate: predicate) + if let distinctMonths = distinct as? [[String : Int]] { + + return distinctMonths.compactMap { + if let month = $0["month"], + let year = $0["year"] { + return Month(month: month, year: year) + } else { + Logger.w("issue with dictionary \($0)") + return nil + } + } + } else { + Logger.w("Could not cast \(distinct) as [Int]") + return [] + } + + } + + static func oldestDate(context: NSManagedObjectContext, activity: Activity) -> Date? { + + let request = Record.fetchRequest() + request.sortDescriptors = [NSSortDescriptor(key: "start", ascending: true)] + request.predicate = NSPredicate(format: "activity = %@", activity) + + do { + let records: [Record] = try context.fetch(request) + return records.first?.start + } catch { + Logger.error(error) + } + return nil + } + + } diff --git a/LeCountdown/Model/NSManagedContext+Extensions.swift b/LeCountdown/Model/NSManagedContext+Extensions.swift index bbbaadf..92ed365 100644 --- a/LeCountdown/Model/NSManagedContext+Extensions.swift +++ b/LeCountdown/Model/NSManagedContext+Extensions.swift @@ -17,7 +17,7 @@ extension NSManagedObjectContext { } func distinct(entityName: String, attributes: [String], predicate: NSPredicate? = nil) throws -> [Any] { - + let request = NSFetchRequest(entityName: entityName) request.entity = NSEntityDescription.entity(forEntityName: entityName, in: self) @@ -47,6 +47,19 @@ extension NSManagedObjectContext { } } + // public func fetch(_ request: NSFetchRequest) throws -> [T] where T : NSFetchRequestResult + + + func fetch(entityName: String, predicate: NSPredicate) -> [T] where T : NSFetchRequestResult { + let request = NSFetchRequest(entityName: entityName) + request.predicate = predicate + do { + return try self.fetch(request) + } catch { + return [] + } + } + } extension NSManagedObject { @@ -58,5 +71,9 @@ extension NSManagedObject { var stringId: String { return self.objectID.uriRepresentation().absoluteString } + + static var entityName: String { + return self.entity().managedObjectClassName + } } diff --git a/LeCountdown/Model/Persistence.swift b/LeCountdown/Model/Persistence.swift index ed1934b..7756d3f 100644 --- a/LeCountdown/Model/Persistence.swift +++ b/LeCountdown/Model/Persistence.swift @@ -31,10 +31,18 @@ struct PersistenceController { countdown.image = CoolPic.pic1.rawValue } - for i in 0..<14 { + for i in 0..<20 { let record = Record(context: viewContext) - record.start = Date() - record.end = Date() + + let randomMonth = (0...10 * 31).randomElement() ?? 3 + let monthTimeInterval = Double(randomMonth) * 3600 * 24 + + let start = Date().addingTimeInterval(-monthTimeInterval) + let duration = Double((1...10).randomElement() ?? 5) * 60.0 + let end = start.addingTimeInterval(duration) + + record.start = start + record.end = end record.activity = activities.randomElement() } diff --git a/LeCountdown/Stats/Filter.swift b/LeCountdown/Stats/Filter.swift index 1c9ddcb..9784245 100644 --- a/LeCountdown/Stats/Filter.swift +++ b/LeCountdown/Stats/Filter.swift @@ -55,11 +55,13 @@ fileprivate extension Int { } -struct Month { +struct Month: Identifiable { var month: Int var year: Int + var id: Int { return self.year * 1000 + month } + var start: NSDate { let components: DateComponents = DateComponents(year: self.year, month: self.month) if let date = Calendar.current.date(from: components) { diff --git a/LeCountdown/Utils/Date+Extensions.swift b/LeCountdown/Utils/Date+Extensions.swift index 7c639a9..c7f8616 100644 --- a/LeCountdown/Utils/Date+Extensions.swift +++ b/LeCountdown/Utils/Date+Extensions.swift @@ -7,6 +7,12 @@ import Foundation +extension Date: Identifiable { + + public var id: TimeInterval { return self.timeIntervalSince1970 } + +} + extension Date { static let monthYearFormatter = { @@ -33,6 +39,12 @@ extension Date { return Calendar.current.startOfDay(for: self) } + var startOfMonth: Date { + let calendar = Calendar(identifier: .gregorian) + let components = calendar.dateComponents([.year, .month], from: self) + return calendar.date(from: components)! + } + func get(_ component: Calendar.Component, calendar: Calendar = Calendar.current) -> Int { return calendar.component(component, from: self) } @@ -41,7 +53,7 @@ extension Date { var month: Int { self.get(.month) } var formattedYear: String { return "\(self.year)" } - var formattedMonth: String { return Date.monthYearFormatter.string(from: self) } + var formattedMonth: String { return Date.monthYearFormatter.string(from: self).capitalized } var formattedDay: String { return Date.dayFormatter.string(from: self) } var formattedDateTime: String { return Date.dateTimeFormatter.string(from: self) } diff --git a/LeCountdown/Views/Reusable/CalendarView.swift b/LeCountdown/Views/Reusable/CalendarView.swift new file mode 100644 index 0000000..96fee37 --- /dev/null +++ b/LeCountdown/Views/Reusable/CalendarView.swift @@ -0,0 +1,132 @@ +// +// CalendarView.swift +// LeCountdown +// +// Created by Laurent Morvillier on 12/04/2023. +// +import SwiftUI + +fileprivate extension DateFormatter { + static var month: DateFormatter { + let formatter = DateFormatter() + formatter.dateFormat = "MMMM" + return formatter + } + + static var monthAndYear: DateFormatter { + let formatter = DateFormatter() + formatter.dateFormat = "MMMM yyyy" + return formatter + } +} + +fileprivate extension Calendar { + func generateDates( + inside interval: DateInterval, + matching components: DateComponents + ) -> [Date] { + var dates: [Date] = [] + dates.append(interval.start) + + enumerateDates( + startingAfter: interval.start, + matching: components, + matchingPolicy: .nextTime + ) { date, _, stop in + if let date = date { + if date < interval.end { + dates.append(date) + } else { + stop = true + } + } + } + return dates + } +} + +struct CalendarView: View where DateView: View { + + let calendar: Calendar = Calendar.current + + let interval: DateInterval + let showHeaders: Bool + + let content: (Date) -> DateView + + init( + interval: DateInterval, + showHeaders: Bool = true, + @ViewBuilder content: @escaping (Date) -> DateView + ) { + self.interval = interval + self.showHeaders = showHeaders + self.content = content + + self.months = calendar.generateDates( + inside: interval, + matching: DateComponents(day: 1, hour: 0, minute: 0, second: 0) + ) + + for month in self.months { + if showHeaders { + self.monthHeaders += [month.formattedMonth] + } + self.monthToDays[month] = self.days(for: month) + } + } + + var body: some View { + LazyVGrid(columns: Array(repeating: GridItem(), count: 7)) { + ForEach(Array(months.enumerated().reversed()), id: \.offset) { (index,month) in + Section(header: self.headerView(for: monthHeaders[index])) { + if let days = self.monthToDays[month] { + ForEach(days, id: \.self) { date in + if calendar.isDate(date, equalTo: month, toGranularity: .month) { + content(date).id(date) + } else { + content(date).hidden() + } + } + } + } + } + } + } + + private var months = [Date]() + private var monthHeaders = [String]() + private var monthToDays = [Date: [Date]]() + + // Important: dont add padding or any frame modifications to the headerView or you will get performance issues + private func headerView(for month:String) -> some View { + Text(month).font(.title) + } + + private func header(for month: Date) -> String { + let component = calendar.component(.month, from: month) + let formatter = component == 1 ? DateFormatter.monthAndYear : .month + return formatter.string(from: month) + } + + private func days(for month: Date) -> [Date] { + guard + let monthInterval = calendar.dateInterval(of: .month, for: month), + let monthFirstWeek = calendar.dateInterval(of: .weekOfMonth, for: monthInterval.start), + let monthLastWeek = calendar.dateInterval(of: .weekOfMonth, for: monthInterval.end) + else { return [] } + return calendar.generateDates( + inside: DateInterval(start: monthFirstWeek.start, end: monthLastWeek.end), + matching: DateComponents(hour: 0, minute: 0, second: 0) + ) + } +} + +struct CalendarView_Previews: PreviewProvider { + static var previews: some View { + CalendarView(interval: DateInterval(start: Date(timeIntervalSinceNow: -2 * 31 * 24 * 3600), end: Date())) { date in + Text(date.get(.day).formatted()) + .padding(8) + } + } +} diff --git a/LeCountdown/Views/Stats/ActivitiesView.swift b/LeCountdown/Views/Stats/ActivitiesView.swift index 2d07a2a..f3d022d 100644 --- a/LeCountdown/Views/Stats/ActivitiesView.swift +++ b/LeCountdown/Views/Stats/ActivitiesView.swift @@ -31,14 +31,19 @@ struct ActivitiesView: View { List { ForEach(self.activities) { activity in - HStack { - Text(activity.name ?? "no activity") - Spacer() - Text(activity.recordCount) - .font(.system(.body, weight: .bold)) - .foregroundColor(.accentColor) + NavigationLink { + ActivityCalendarView(activity: activity) + } label: { + HStack { + Text(activity.name ?? "no activity") + Spacer() + Text(activity.recordCount) + .font(.system(.body, weight: .bold)) + .foregroundColor(.accentColor) + } } + // NavigationLink { // ActivityView(activity: activity) // .environment(\.managedObjectContext, viewContext) diff --git a/LeCountdown/Views/Stats/ActivityCalendarView.swift b/LeCountdown/Views/Stats/ActivityCalendarView.swift new file mode 100644 index 0000000..d2c6b34 --- /dev/null +++ b/LeCountdown/Views/Stats/ActivityCalendarView.swift @@ -0,0 +1,111 @@ +// +// TestActivityView.swift +// LeCountdown +// +// Created by Laurent Morvillier on 11/04/2023. +// + +import SwiftUI + +struct MonthView: View { + + var date: Date + + fileprivate var days: [String] = Calendar.current.shortWeekdaySymbols + + init(date: Date) { + self.date = date + + // put Sunday at the end + self.days.append(self.days.removeFirst()) + } + + var body: some View { + + Text(self.date.formattedMonth).font(.title) + + HStack { + ForEach(0..<7) { day in + Text(self.days[day]).frame(maxWidth: .infinity) + } + } + + } + +} + +struct ActivityCalendarView: View { + + @Environment(\.calendar) var calendar + + var activity: Activity + + @State var monthlyDates: [Date] = [] + + @State var oldestDate: Date? = nil + + fileprivate let today = Date() + + @Environment(\.managedObjectContext) private var viewContext + + @State var recordsDate: [Date] = [] + + var body: some View { + + ScrollView { + if let oldestDate { + CalendarView(interval: DateInterval(start: oldestDate, end: today)) { date in + + ZStack { + + if self._hasRecord(date: date) { + Circle().fill(Color.green).brightness(0.5) + } + Text(date.get(.day).formatted()) + }.padding(6.0) + + } + } + } + .navigationTitle(activity.name ?? "Activity") + .onAppear { + self._load() + } + + } + + fileprivate func _load() { + + self.oldestDate = CoreDataRequests.oldestDate(context: self.viewContext, activity: self.activity) + + let predicate = NSPredicate(format: "activity = %@", self.activity) + let records: [Record] = self.viewContext.fetch(entityName: Record.entityName, predicate: predicate) + self.recordsDate = records.compactMap { $0.start } + + } + + fileprivate func _hasRecord(date: Date) -> Bool { + self.recordsDate.contains { rd in + self.calendar.isDate(rd, inSameDayAs: date) + } + } + +} + +struct ActivityCalendarView_Previews: PreviewProvider { + + static var activity: Activity { + let context = PersistenceController.preview.container.viewContext + do { + return try context.fetch(Activity.fetchRequest()).first ?? Activity.fake(context: context) + } catch { + return Activity.fake(context: context) + } + } + + static var previews: some View { + ActivityCalendarView(activity: self.activity) + .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) + } + +} diff --git a/LeCountdown/Views/Stats/ActivityView.swift b/LeCountdown/Views/Stats/ActivityView.swift index fa2eeaa..195696f 100644 --- a/LeCountdown/Views/Stats/ActivityView.swift +++ b/LeCountdown/Views/Stats/ActivityView.swift @@ -76,22 +76,26 @@ enum TimeFrame: Int, Identifiable, CaseIterable { Logger.w("Could not cast \(distinct) as [Int]") } case .month: - let distinct = try context.distinct(entityName: "Record", attributes: ["year", "month"], predicate: predicate) - if let distinctMonths = distinct as? [[String : Int]] { - - let months = distinctMonths.compactMap { - if let month = $0["month"], - let year = $0["year"] { - return Month(month: month, year: year) - } else { - Logger.w("issue with dictionary \($0)") - return nil - } - } - return months.map { Filter.month($0) } - } else { - Logger.w("Could not cast \(distinct) as [Int]") - } + + let monthes = try CoreDataRequests.months(context: context, activity: activity) + return monthes.map { Filter.month($0) } + +// let distinct = try context.distinct(entityName: "Record", attributes: ["year", "month"], predicate: predicate) +// if let distinctMonths = distinct as? [[String : Int]] { +// +// let months = distinctMonths.compactMap { +// if let month = $0["month"], +// let year = $0["year"] { +// return Month(month: month, year: year) +// } else { +// Logger.w("issue with dictionary \($0)") +// return nil +// } +// } +// return months.map { Filter.month($0) } +// } else { +// Logger.w("Could not cast \(distinct) as [Int]") +// } } } catch { Logger.error(error)