parent
df5bb8c8d4
commit
2eb0232bf7
@ -0,0 +1,133 @@ |
|||||||
|
// |
||||||
|
// AppEnvironment.swift |
||||||
|
// LeCountdown |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 31/01/2023. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import ActivityKit |
||||||
|
|
||||||
|
class Conductor : ObservableObject { |
||||||
|
|
||||||
|
static let maestro: Conductor = Conductor() |
||||||
|
|
||||||
|
@UserDefault(Key.dates.rawValue, defaultValue: [:]) static var savedDates: [String : DateInterval] |
||||||
|
|
||||||
|
init() { |
||||||
|
self.notificationDates = Conductor.savedDates |
||||||
|
} |
||||||
|
|
||||||
|
@Published var notificationDates: [String : DateInterval] = [:] { |
||||||
|
didSet { |
||||||
|
Conductor.savedDates = notificationDates |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
enum Key : String { |
||||||
|
case dates |
||||||
|
} |
||||||
|
|
||||||
|
func startCountdown(_ date: Date, countdown: Countdown) { |
||||||
|
DispatchQueue.main.async { |
||||||
|
let dateInterval = DateInterval(start: Date(), end: date) |
||||||
|
self.notificationDates[countdown.stringId] = dateInterval |
||||||
|
|
||||||
|
self._launchLiveActivity(countdown: countdown, endDate: date) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func notifyUser(countdownId: String, cancel: Bool) { |
||||||
|
// self._playSound(countdownId: countdownId) |
||||||
|
endCountdown(countdownId: countdownId, cancel: cancel) |
||||||
|
} |
||||||
|
|
||||||
|
func endCountdown(countdownId: String, cancel: Bool) { |
||||||
|
DispatchQueue.main.async { |
||||||
|
if !cancel { |
||||||
|
self._recordActivity(countdownId: countdownId) |
||||||
|
} |
||||||
|
self.notificationDates.removeValue(forKey: countdownId) |
||||||
|
|
||||||
|
self._endLiveActivity(countdownId: countdownId) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func cleanup() { |
||||||
|
let now = Date() |
||||||
|
for (key, value) in self.notificationDates { |
||||||
|
if value.end < now { |
||||||
|
self.endCountdown(countdownId: key, cancel: false) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fileprivate func _recordActivity(countdownId: String) { |
||||||
|
let context = PersistenceController.shared.container.viewContext |
||||||
|
if let countdown = context.object(stringId: countdownId) as? Countdown, |
||||||
|
let dateInterval = self.notificationDates[countdownId] { |
||||||
|
do { |
||||||
|
try CoreDataRequests.recordActivity(countdown: countdown, dateInterval: dateInterval) |
||||||
|
} catch { |
||||||
|
print("Could not record activity = \(error)") |
||||||
|
// TODO: show error to user |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// MARK: - Live Activity |
||||||
|
|
||||||
|
fileprivate func _launchLiveActivity(countdown: Countdown, endDate: Date) { |
||||||
|
|
||||||
|
if ActivityAuthorizationInfo().areActivitiesEnabled { |
||||||
|
|
||||||
|
let contentState = LaunchWidgetAttributes.ContentState(ended: false) |
||||||
|
let attributes = LaunchWidgetAttributes(id: countdown.stringId, name: countdown.displayName, endDate: endDate) |
||||||
|
let activityContent = ActivityContent(state: contentState, staleDate: endDate.addingTimeInterval(30.0)) |
||||||
|
|
||||||
|
do { |
||||||
|
let liveActivity = try ActivityKit.Activity.request(attributes: attributes, content: activityContent) |
||||||
|
print("Requested a countdown Live Activity \(String(describing: liveActivity.id)).") |
||||||
|
} catch (let error) { |
||||||
|
print("Error requesting countdown Live Activity \(error.localizedDescription).") |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fileprivate func _liveActivity(countdownId: String) -> ActivityKit.Activity<LaunchWidgetAttributes>? { |
||||||
|
return ActivityKit.Activity<LaunchWidgetAttributes>.activities.first(where: { $0.attributes.id == countdownId } ) |
||||||
|
} |
||||||
|
|
||||||
|
func updateLiveActivities() { |
||||||
|
|
||||||
|
for (countdownId, interval) in self.notificationDates { |
||||||
|
if let activity = self._liveActivity(countdownId: countdownId) { |
||||||
|
|
||||||
|
Task { |
||||||
|
let ended = interval.end < Date() |
||||||
|
let state = LaunchWidgetAttributes.ContentState(ended: ended) |
||||||
|
let content = ActivityContent(state: state, staleDate: interval.end) |
||||||
|
await activity.update(content) |
||||||
|
print("Ending the Live Activity: \(activity.id)") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
fileprivate func _endLiveActivity(countdownId: String) { |
||||||
|
|
||||||
|
print("Trt to end the Live Activity: \(countdownId)") |
||||||
|
|
||||||
|
if let activity = self._liveActivity(countdownId: countdownId) { |
||||||
|
Task { |
||||||
|
let state = LaunchWidgetAttributes.ContentState(ended: true) |
||||||
|
let content = ActivityContent(state: state, staleDate: Date()) |
||||||
|
await activity.end(content, dismissalPolicy: .immediate) |
||||||
|
print("Ending the Live Activity: \(activity.id)") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Binary file not shown.
Loading…
Reference in new issue