Reputation: 41
I'm trying to implement a count down timer in my app that also utilizes the live activity api. When the app is minimized the timer runs in the dynamic island / lockscreen. However I have not figured out yet how to end the live activity when the timer is finished and the app is in the background, it just keeps counting up after it reached the end date.
This is the state i use:
struct TimerAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
var willEndAt: Date?
var pausedAt: Date?
func getElapsedTimeInSeconds() -> Int {
let now = Date()
guard let willEndAt = self.willEndAt else {
return 0
}
guard let pausedAt = self.pausedAt else {
return Int(now.timeIntervalSince1970 - willEndAt.timeIntervalSince1970)
}
return Int(pausedAt.timeIntervalSince1970 - willEndAt.timeIntervalSince1970)
}
func getPausedTime() -> String {
let elapsedTimeInSeconds = abs(getElapsedTimeInSeconds())
let minutes = (elapsedTimeInSeconds % 3600) / 60
let seconds = elapsedTimeInSeconds % 60
return String(format: "%d:%02d", minutes, seconds)
}
func getTimeIntervalSinceNow() -> Double {
guard let willEndAt = self.willEndAt else {
return 0
}
return willEndAt.timeIntervalSince1970 - Date().timeIntervalSince1970
}
func isRunning() -> Bool {
return pausedAt == nil && willEndAt != nil
}
func isPaused() -> Bool {
return pausedAt != nil
}
}
}
And this is how i display the timer:
Text(
Date(timeIntervalSinceNow: context.state.getTimeIntervalSinceNow()),
style: .timer
)
Since i also schedule a local notification when the timer is finished my first thought was to somehow cancel the live activity when the notification is received but that only works when the user clicks on the notification or the app is in the foreground.
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
Task {
for activity in Activity<TimerAttributes>.activities {
await activity.end(nil, dismissalPolicy: .immediate)
}
}
completionHandler([.banner, .sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
Task {
for activity in Activity<TimerAttributes>.activities {
await activity.end(nil, dismissalPolicy: .immediate)
}
}
completionHandler()
}
I also tried using the "staleDate" attribute in the ActivityContent however that also does not end the live activity.
How do you stop the live activity when the app is in the background without using remote push notifications (since the timer should also work when the user has no internet)?
Upvotes: 2
Views: 597
Reputation: 114
Have you tried starting the live activity and immediately ending it with dismissal policy set to your preferred finish date?
activeActivity.end(
ActivityContent(state: TimerAttributes.ContentState(willEndAt: _END_DATE_, pausedAt: _PAUSE_DATE_), staleDate: nil),
dismissalPolicy: .after(date.now.addingTimeInterval(_SECONDS_TO_END_DATE_))
)
Upvotes: 0