-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
51e2e43
commit 0531400
Showing
4 changed files
with
162 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
// | ||
// Copyright 2025 Mikhail Kasianov | ||
// | ||
// Use of this source code is governed by an MIT-style | ||
// license that can be found in the LICENSE file or at | ||
// https://opensource.org/licenses/MIT. | ||
|
||
import CoreData | ||
|
||
extension UserActivity { | ||
|
||
/// Triggers the creation of `UserActivity` records for the current date. | ||
/// | ||
/// This method checks if `UserActivity` records already exist for the current date. | ||
/// If no records exist, it creates new `UserActivity` records and saves them to the context. | ||
/// | ||
/// - Parameter context: The managed object context in which to perform the operation. | ||
/// - Throws: An error if the operation fails. | ||
/// | ||
static func trigger(in context: NSManagedObjectContext) throws { | ||
try trigger(date: Date(), in: context) | ||
} | ||
|
||
/// Triggers the creation of `UserActivity` records for the specified date. | ||
/// | ||
/// This method checks if `UserActivity` records already exist for the specified date. | ||
/// If no records exist, it creates new `UserActivity` records and saves them to the context. | ||
/// | ||
/// - Parameters: | ||
/// - date: The date for which to create the `UserActivity` records. | ||
/// - context: The managed object context in which to perform the operation. | ||
/// - Throws: An error if the operation fails. | ||
/// | ||
static func trigger(date: Date, in context: NSManagedObjectContext) throws { | ||
let activities = try activities(for: date, in: context) | ||
|
||
for period in ActivityPeriod.allCases { | ||
let limit = date.startOfDay.adding(period.rangeComponent) | ||
|
||
for activity in activities { | ||
if let day = activity.day, day < limit { | ||
activity[keyPath: period.countField] = 1 | ||
} | ||
} | ||
} | ||
|
||
try context.save() | ||
} | ||
} | ||
|
||
// MARK: - Private Functions | ||
|
||
/// Retrieves or creates user activities for a given date within a specified context. | ||
/// | ||
/// This function first determines the range of dates for the given date, starting from the | ||
/// beginning of the day and extending to the end of the month. It then attempts to fetch existing | ||
/// activities within this range from the provided context. If there are gaps in the activities, | ||
/// it creates new activities to fill those gaps. | ||
/// | ||
/// - Parameters: | ||
/// - date: The date for which to retrieve or create activities. | ||
/// - context: The managed object context used to fetch or create activities. | ||
/// | ||
/// - Returns: An array of `UserActivity` objects for the specified date range. | ||
/// | ||
/// - Throws: An error if there is an issue fetching existing activities from the context. | ||
/// | ||
private func activities(for date: Date, in context: NSManagedObjectContext) throws -> [UserActivity] | ||
{ | ||
let range = date.startOfDay..<date.startOfDay.addingMonth() | ||
|
||
var activities = try existing(for: range, in: context) | ||
var recent = activities.last?.day?.addingDay() ?? date.startOfDay | ||
|
||
while recent < range.upperBound { | ||
let activity = newActivity(date: recent, in: context) | ||
activities.append(activity) | ||
recent = recent.addingDay() | ||
} | ||
|
||
return activities | ||
} | ||
|
||
private func existing(for range: Range<Date>, in context: NSManagedObjectContext) throws | ||
-> [UserActivity] | ||
{ | ||
let request = UserActivity.fetchRequest() | ||
|
||
request.sortDescriptors = [ | ||
NSSortDescriptor( | ||
keyPath: \UserActivity.day, | ||
ascending: true | ||
) | ||
] | ||
|
||
request.predicate = NSPredicate( | ||
format: "day >= %@ AND day < %@", | ||
range.lowerBound as NSDate, | ||
range.upperBound as NSDate | ||
) | ||
|
||
return try context.fetch(request) | ||
} | ||
|
||
private func newActivity(date: Date, in context: NSManagedObjectContext) -> UserActivity { | ||
let entity = NSEntityDescription.entity(forEntityName: "UserActivity", in: context)! | ||
let activeUser = UserActivity(entity: entity, insertInto: context) | ||
|
||
activeUser.date = date | ||
activeUser.day = date.startOfDay | ||
activeUser.week = date.startOfWeek | ||
activeUser.month = date.startOfMonth | ||
|
||
return activeUser | ||
} | ||
|
||
// MARK: - Count Field | ||
|
||
extension ActivityPeriod { | ||
|
||
/// A computed property that returns the appropriate count field key path for the activity period. | ||
/// | ||
/// This property provides the key path to the count field (`dayCount`, `weekCount`, or `monthCount`) | ||
/// based on the activity period (`daily`, `weekly`, or `monthly`). | ||
/// | ||
fileprivate var countField: ReferenceWritableKeyPath<UserActivity, Int32> { | ||
switch self { | ||
case .daily: | ||
return \.dayCount | ||
case .weekly: | ||
return \.weekCount | ||
case .monthly: | ||
return \.monthCount | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters