Updated the documentation of the README file in the project.
This commit is contained in:
@@ -418,8 +418,8 @@ private extension RecordingView.Model {
|
||||
}
|
||||
|
||||
/// Starts listening to the commands sent through the commander, unless already listening: every received command is translated
|
||||
/// into the button press it mirrors — ``RecordingCommand/toggle`` presses the main button, and ``RecordingCommand/discard``
|
||||
/// presses the discard button.
|
||||
/// into the button press it mirrors — ``RecordingCommand/toggle`` presses the main button, ``RecordingCommand/discard``
|
||||
/// presses the discard button, and ``RecordingCommand/process`` presses the send button.
|
||||
func listenToCommands() {
|
||||
guard taskCommands == nil else {
|
||||
return
|
||||
|
||||
@@ -54,11 +54,11 @@ Attendi/
|
||||
│ └── Attendi/ # The widget extension target
|
||||
│ ├── Sources/
|
||||
│ │ ├── Bundle/ # The @main widget bundle
|
||||
│ │ ├── Extensions/ # The system symbol images shared across the extension
|
||||
│ │ ├── Extensions/ # The shared symbol images, and the state-to-symbol/tint presentation mapping
|
||||
│ │ ├── Intents/ # The toggle, process, and discard App Intents behind the activity's buttons
|
||||
│ │ ├── Styles/ # The ActionButtonStyle button style
|
||||
│ │ ├── Views/ # The activity's timer text, state label, controls, and Lock Screen banner
|
||||
│ │ └── Widgets/ # The RecordingLiveActivity configuration, and the template AttendiWidget placeholder
|
||||
│ │ └── Widgets/ # The RecordingLiveActivity configuration
|
||||
│ ├── Catalogs/ # The extension's assets and localized strings
|
||||
│ └── Resources/ # The extension's Info.plist
|
||||
└── Packages/
|
||||
@@ -76,7 +76,6 @@ Attendi/
|
||||
│ │ └── View Models/ # The Notifier view model
|
||||
│ └── Recording/ # The Recording feature target
|
||||
│ ├── Catalogs/ # The record, pause, and send icons; the localized strings
|
||||
│ ├── Clocks/ # The AnyClock eraser behind the model's injectable clock
|
||||
│ ├── Errors/ # The AudioCapturingError, AudioTranscribingError, RecordingError errors
|
||||
│ ├── Models/ # The Transcription model
|
||||
│ ├── Protocols/ # The Capturing, Preinstalling, Reporting, Transcribing protocols
|
||||
@@ -189,11 +188,10 @@ machine as a tap in the app.
|
||||
|
||||
### Timer
|
||||
|
||||
The recording time is measured against a monotonic clock injected at initialization — the `ContinuousClock` by
|
||||
default, a manual clock in the tests: an anchor marks the start of the current recording
|
||||
stretch, and pausing folds the stretch into an accumulated duration, so scheduling latency never accumulates as drift.
|
||||
While recording, an async task republishes the measured time once per second. The view formats the count as `mm:ss`
|
||||
and animates digit changes with a numeric text content transition.
|
||||
The recording time is measured against a `ContinuousClock`: an anchor marks the start of the current recording
|
||||
stretch, and pausing folds the stretch into an accumulated duration, so scheduling latency never accumulates as
|
||||
drift. While recording, an async task republishes the measured time once per second. The view formats the count as
|
||||
`mm:ss` and animates digit changes with a numeric text content transition.
|
||||
|
||||
### Errors
|
||||
|
||||
@@ -208,10 +206,9 @@ distinguished from an ordinary transcription failure. The underlying errors are
|
||||
the model's current state, forwards every transcription to a closure given at initialization, and presents the
|
||||
error alert. All controls carry localized accessibility labels matching their current action, and the timer
|
||||
exposes its value to assistive technologies spelled out in full units.
|
||||
- **`RecordingButtonStyle`** — a custom `ButtonStyle` used by the controls: the label sits on a padded, circular red
|
||||
Liquid Glass background that reacts fluidly to presses, and dims while disabled. The label and its padding scale
|
||||
with Dynamic Type via `@ScaledMetric`, and an `invertStyle` flag controls whether the label's color scheme is
|
||||
inverted for contrast.
|
||||
- **`RecordingButtonStyle`** — a custom `ButtonStyle` used by the controls: the label sits on a padded, circular
|
||||
Liquid Glass background — tinted by the color given at initialization, red by default — that reacts fluidly to
|
||||
presses, and dims while disabled. The label and its padding scale with Dynamic Type via `@ScaledMetric`.
|
||||
|
||||
### Services
|
||||
|
||||
@@ -249,11 +246,12 @@ mocks, and other backends can be plugged in the same way without touching the fe
|
||||
|
||||
`ContentView` hosts the feature inside a navigation stack and presents the transcribed text of every processed
|
||||
recording in a modal sheet — or a content-unavailable message when the transcription is empty. A toolbar menu picks
|
||||
the locale of the spoken language from the locales the `Preinstalling` service supports; the pick persists across
|
||||
launches in the user defaults and is restored — realigned to the supported locales — before the feature loads,
|
||||
starting from the closest supported equivalent of the user's locale on a first launch. Picking a locale preinstalls
|
||||
its speech model assets in the background through the service, so the first transcription does not have to download
|
||||
them mid-processing.
|
||||
the locale of the spoken language from the locales the `Preinstalling` service supports, each named by its localized
|
||||
language name paired with its region's flag emoji; the pick persists across launches in the user defaults and is
|
||||
restored — realigned to the supported locales — before the feature loads, starting from the closest supported
|
||||
equivalent of the user's locale on a first launch. The navigation title tracks the pick, reading "Transcribe to" the
|
||||
locale's name. Picking a locale preinstalls its speech model assets in the background through the service, so the
|
||||
first transcription does not have to download them mid-processing.
|
||||
|
||||
The preinstallation runs as a cancellable task owned by the model: picking another locale cancels a download still in
|
||||
flight before starting the new one. The model maps the service's started, cancelled, and failed download events to the
|
||||
@@ -274,7 +272,9 @@ On iOS, the recording flow surfaces outside the app through a Live Activity, bui
|
||||
- **`ActivityReporting`** (app target) implements the `Recording` feature's `Reporting` port over `ActivityKit`: it
|
||||
starts a Live Activity when a recording starts, updates its content on every pause, resumption, and processing
|
||||
transition, and ends it — dismissing it immediately — when the flow ends. The activity is an auxiliary surface,
|
||||
never a required one: a start the user disallowed or the system refused is silently ignored.
|
||||
never a required one: a start the user disallowed or the system refused is silently ignored. Every update carries
|
||||
a stale date, and a launch-time reset ends any activity a previous run left behind — a kill before it could end
|
||||
its own — so an abandoned activity never lingers.
|
||||
- **The `Commanding` target** (package) is the vocabulary the app and the widget extension share: the activity's
|
||||
attributes and content state — the flow's state, the timer anchor, and the frozen elapsed time — the commands
|
||||
that control the flow, and the `RecordingCommander` bridge that carries them. The commander exists because the
|
||||
@@ -285,9 +285,10 @@ On iOS, the recording flow surfaces outside the app through a Live Activity, bui
|
||||
system itself counts up from the content state's anchor, formatted as `mm:ss`, so a running recording needs no
|
||||
content updates to tick — `RecordingStateLabel`, and `RecordingControls`, whose buttons wear the circular,
|
||||
color-coded `ActionButtonStyle` and fire the toggle, process, and discard `LiveActivityIntent`s. The system
|
||||
executes those intents in the app's process, where each sends its command through the shared commander and the
|
||||
view model handles it exactly like a press of the matching button — closing the loop. The Lock Screen banner
|
||||
keeps the system's default background material, Liquid Glass.
|
||||
executes every intent in the app's process, where it sends its command through the shared commander and the
|
||||
view model handles it exactly like a press of the matching button; the toggle intent runs there silently,
|
||||
pausing or resuming without leaving the Live Activity, while the process and discard intents bring the app to
|
||||
the foreground. The Lock Screen banner keeps the system's default background material, Liquid Glass.
|
||||
|
||||
## Testing
|
||||
|
||||
@@ -298,12 +299,11 @@ model. In the `Notifying` target, `NotifierTests.swift` covers the posting and a
|
||||
injected, shortened dismissal delay, and `AppNotificationTests.swift` covers the `AppNotification` model. In the
|
||||
`Commanding` target, `RecordingCommanderTests.swift` covers the ordered delivery of the commands. The state-dependent
|
||||
behaviors are exercised with parameterized tests across all four states, driven through the model's public press
|
||||
handlers, and the service mocks record their invocations in call order. The timer behaviors run against a manual test
|
||||
clock advanced by hand, so no test sleeps in real time to see a tick. `AudioCapturingTests.swift` covers the one piece of pure logic in
|
||||
the audio services — the parsing of audio session interruption notifications into capturing events, extracted so it
|
||||
compiles on every platform. The rest of the system-bound service code, `AssetPreinstalling` included, is deliberately
|
||||
untested: thin wrappers over device-global frameworks like `AssetInventory` and `SpeechAnalyzer`, whose behavior mocks
|
||||
cannot meaningfully verify.
|
||||
handlers, and the service mocks record their invocations in call order. `AudioCapturingTests.swift` covers the one
|
||||
piece of pure logic in the audio services — the parsing of audio session interruption notifications into capturing
|
||||
events, extracted so it compiles on every platform. The rest of the system-bound service code, `AssetPreinstalling`
|
||||
included, is deliberately untested: thin wrappers over device-global frameworks like `AssetInventory` and
|
||||
`SpeechAnalyzer`, whose behavior mocks cannot meaningfully verify.
|
||||
|
||||
Run the package tests with the `Features` scheme in Xcode (`⌘U`), or from the command line. The suites run on any of the
|
||||
three platforms; macOS needs no simulator:
|
||||
@@ -316,8 +316,9 @@ For iOS, point the destination at a booted simulator, for example `-destination
|
||||
|
||||
The app has its own `AttendiTests` unit-test bundle, hosted by the app: `ContentViewModelTests.swift` drives
|
||||
`ContentView.Model` with a mocked `Preinstalling` service, covering the locale loading and alignment, the
|
||||
cancellable preinstallation guard, and the mapping of the download events to the notifier's notifications. Run it
|
||||
with the `Attendi` scheme in Xcode (`⌘U`).
|
||||
cancellable preinstallation guard, the mapping of the download events to the notifier's notifications, the locale
|
||||
naming — pairing the localized language name with its region's flag emoji — and the navigation title tracking the
|
||||
picked locale. Run it with the `Attendi` scheme in Xcode (`⌘U`).
|
||||
|
||||
## Localization
|
||||
|
||||
|
||||
Reference in New Issue
Block a user