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
|
/// 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``
|
/// into the button press it mirrors — ``RecordingCommand/toggle`` presses the main button, ``RecordingCommand/discard``
|
||||||
/// presses the discard button.
|
/// presses the discard button, and ``RecordingCommand/process`` presses the send button.
|
||||||
func listenToCommands() {
|
func listenToCommands() {
|
||||||
guard taskCommands == nil else {
|
guard taskCommands == nil else {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -54,11 +54,11 @@ Attendi/
|
|||||||
│ └── Attendi/ # The widget extension target
|
│ └── Attendi/ # The widget extension target
|
||||||
│ ├── Sources/
|
│ ├── Sources/
|
||||||
│ │ ├── Bundle/ # The @main widget bundle
|
│ │ ├── 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
|
│ │ ├── Intents/ # The toggle, process, and discard App Intents behind the activity's buttons
|
||||||
│ │ ├── Styles/ # The ActionButtonStyle button style
|
│ │ ├── Styles/ # The ActionButtonStyle button style
|
||||||
│ │ ├── Views/ # The activity's timer text, state label, controls, and Lock Screen banner
|
│ │ ├── 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
|
│ ├── Catalogs/ # The extension's assets and localized strings
|
||||||
│ └── Resources/ # The extension's Info.plist
|
│ └── Resources/ # The extension's Info.plist
|
||||||
└── Packages/
|
└── Packages/
|
||||||
@@ -76,7 +76,6 @@ Attendi/
|
|||||||
│ │ └── View Models/ # The Notifier view model
|
│ │ └── View Models/ # The Notifier view model
|
||||||
│ └── Recording/ # The Recording feature target
|
│ └── Recording/ # The Recording feature target
|
||||||
│ ├── Catalogs/ # The record, pause, and send icons; the localized strings
|
│ ├── 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
|
│ ├── Errors/ # The AudioCapturingError, AudioTranscribingError, RecordingError errors
|
||||||
│ ├── Models/ # The Transcription model
|
│ ├── Models/ # The Transcription model
|
||||||
│ ├── Protocols/ # The Capturing, Preinstalling, Reporting, Transcribing protocols
|
│ ├── Protocols/ # The Capturing, Preinstalling, Reporting, Transcribing protocols
|
||||||
@@ -189,11 +188,10 @@ machine as a tap in the app.
|
|||||||
|
|
||||||
### Timer
|
### Timer
|
||||||
|
|
||||||
The recording time is measured against a monotonic clock injected at initialization — the `ContinuousClock` by
|
The recording time is measured against a `ContinuousClock`: an anchor marks the start of the current recording
|
||||||
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
|
||||||
stretch, and pausing folds the stretch into an accumulated duration, so scheduling latency never accumulates as drift.
|
drift. While recording, an async task republishes the measured time once per second. The view formats the count as
|
||||||
While recording, an async task republishes the measured time once per second. The view formats the count as `mm:ss`
|
`mm:ss` and animates digit changes with a numeric text content transition.
|
||||||
and animates digit changes with a numeric text content transition.
|
|
||||||
|
|
||||||
### Errors
|
### 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
|
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
|
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.
|
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
|
- **`RecordingButtonStyle`** — a custom `ButtonStyle` used by the controls: the label sits on a padded, circular
|
||||||
Liquid Glass background that reacts fluidly to presses, and dims while disabled. The label and its padding scale
|
Liquid Glass background — tinted by the color given at initialization, red by default — that reacts fluidly to
|
||||||
with Dynamic Type via `@ScaledMetric`, and an `invertStyle` flag controls whether the label's color scheme is
|
presses, and dims while disabled. The label and its padding scale with Dynamic Type via `@ScaledMetric`.
|
||||||
inverted for contrast.
|
|
||||||
|
|
||||||
### Services
|
### 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
|
`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
|
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
|
the locale of the spoken language from the locales the `Preinstalling` service supports, each named by its localized
|
||||||
launches in the user defaults and is restored — realigned to the supported locales — before the feature loads,
|
language name paired with its region's flag emoji; the pick persists across launches in the user defaults and is
|
||||||
starting from the closest supported equivalent of the user's locale on a first launch. Picking a locale preinstalls
|
restored — realigned to the supported locales — before the feature loads, starting from the closest supported
|
||||||
its speech model assets in the background through the service, so the first transcription does not have to download
|
equivalent of the user's locale on a first launch. The navigation title tracks the pick, reading "Transcribe to" the
|
||||||
them mid-processing.
|
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
|
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
|
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
|
- **`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
|
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,
|
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
|
- **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
|
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
|
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
|
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,
|
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
|
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
|
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 — closing the loop. The Lock Screen banner
|
view model handles it exactly like a press of the matching button; the toggle intent runs there silently,
|
||||||
keeps the system's default background material, Liquid Glass.
|
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
|
## 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
|
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
|
`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
|
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
|
handlers, and the service mocks record their invocations in call order. `AudioCapturingTests.swift` covers the one
|
||||||
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
|
piece of pure logic in the audio services — the parsing of audio session interruption notifications into capturing
|
||||||
the audio services — the parsing of audio session interruption notifications into capturing events, extracted so it
|
events, extracted so it compiles on every platform. The rest of the system-bound service code, `AssetPreinstalling`
|
||||||
compiles on every platform. The rest of the system-bound service code, `AssetPreinstalling` included, is deliberately
|
included, is deliberately untested: thin wrappers over device-global frameworks like `AssetInventory` and
|
||||||
untested: thin wrappers over device-global frameworks like `AssetInventory` and `SpeechAnalyzer`, whose behavior mocks
|
`SpeechAnalyzer`, whose behavior mocks cannot meaningfully verify.
|
||||||
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
|
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:
|
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
|
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
|
`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
|
cancellable preinstallation guard, the mapping of the download events to the notifier's notifications, the locale
|
||||||
with the `Attendi` scheme in Xcode (`⌘U`).
|
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
|
## Localization
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user