Implemented the ActivityReporting service in the Sample app target to surface the recording flow in Live Activities.
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
#if os(iOS)
|
||||
@preconcurrency import ActivityKit
|
||||
#endif
|
||||
|
||||
import Commanding
|
||||
import Foundation
|
||||
import Recording
|
||||
|
||||
/// The reporting service that surfaces the recording flow in a Live Activity on the Lock Screen and in the Dynamic Island.
|
||||
///
|
||||
/// The service implements the `Recording` feature's `Reporting` port over the Live Activity vocabulary shared with the widget
|
||||
/// extension through the `Commanding` target: it starts a Live Activity when a recording starts, updates its content on every pause,
|
||||
/// resumption, and processing transition, and ends it — dismissing it right away — when the flow ends. The activity is an auxiliary
|
||||
/// surface of the recording flow, never a required one: a start that fails, or that the user disallowed in the system settings, is
|
||||
/// silently ignored, and the platforms without Live Activities reduce the whole service to a no-op.
|
||||
@MainActor
|
||||
final class ActivityReporting: Reporting {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
#if os(iOS)
|
||||
/// The Live Activity reflecting the ongoing recording flow, or `nil` while none is.
|
||||
private var activity: Activity<RecordingActivityAttributes>?
|
||||
#endif
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
/// Creates a reporting service with no Live Activity started yet.
|
||||
init() {}
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
/// Starts a Live Activity for a new recording, unless the user disallowed Live Activities or the system refuses to start one.
|
||||
///
|
||||
/// - Parameter anchor: The instant the elapsed recording time counts from.
|
||||
func started(
|
||||
at anchor: Date
|
||||
) async {
|
||||
#if os(iOS)
|
||||
guard ActivityAuthorizationInfo().areActivitiesEnabled else {
|
||||
return
|
||||
}
|
||||
|
||||
activity = try? Activity.request(
|
||||
attributes: RecordingActivityAttributes(),
|
||||
content: .init(
|
||||
state: .init(
|
||||
state: .recording,
|
||||
anchor: anchor,
|
||||
elapsed: .zero
|
||||
),
|
||||
staleDate: nil
|
||||
)
|
||||
)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Updates the Live Activity with the pause of the ongoing recording, freezing its timer at the elapsed recording time.
|
||||
///
|
||||
/// - Parameter elapsed: The number of seconds spent recording so far, excluding any time spent paused.
|
||||
func paused(
|
||||
elapsed: TimeInterval
|
||||
) async {
|
||||
#if os(iOS)
|
||||
await update(
|
||||
state: .paused,
|
||||
anchor: nil,
|
||||
elapsed: elapsed
|
||||
)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Updates the Live Activity with the resumption of a paused recording, restarting its timer from the given anchor.
|
||||
///
|
||||
/// - Parameter anchor: The instant the elapsed recording time counts from, moved back by the time already spent recording.
|
||||
func resumed(
|
||||
anchor: Date
|
||||
) async {
|
||||
#if os(iOS)
|
||||
await update(
|
||||
state: .recording,
|
||||
anchor: anchor,
|
||||
elapsed: .zero
|
||||
)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Updates the Live Activity with the processing of the recorded input, freezing its timer at the elapsed recording time.
|
||||
///
|
||||
/// - Parameter elapsed: The number of seconds spent recording, excluding any time spent paused.
|
||||
func processing(
|
||||
elapsed: TimeInterval
|
||||
) async {
|
||||
#if os(iOS)
|
||||
await update(
|
||||
state: .processing,
|
||||
anchor: nil,
|
||||
elapsed: elapsed
|
||||
)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Ends the Live Activity of the recording flow, dismissing it right away.
|
||||
func ended() async {
|
||||
#if os(iOS)
|
||||
await activity?.end(
|
||||
activity?.content,
|
||||
dismissalPolicy: .immediate
|
||||
)
|
||||
|
||||
activity = nil
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
// MARK: - Helpers
|
||||
|
||||
private extension ActivityReporting {
|
||||
|
||||
/// Updates the dynamic content of the Live Activity, when one is running.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - state: The state of the recording flow to show.
|
||||
/// - anchor: The instant the elapsed recording time counts from, or `nil` while the recording is not running.
|
||||
/// - elapsed: The number of seconds spent recording up to the transition, excluding any time spent paused.
|
||||
func update(
|
||||
state: RecordingActivityState,
|
||||
anchor: Date?,
|
||||
elapsed: TimeInterval
|
||||
) async {
|
||||
await activity?.update(
|
||||
.init(
|
||||
state: .init(
|
||||
state: state,
|
||||
anchor: anchor,
|
||||
elapsed: elapsed
|
||||
),
|
||||
staleDate: nil
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user