From f1e4b598096eef3813c20a1197502dc2c6b943cd Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Sun, 5 Jul 2026 16:29:28 +0200 Subject: [PATCH] Extracted the PreviewPreinstalling service in the Sample app target into its own file and declared it as development asset. --- Apps/Attendi/Catalogs/Localizable.xcstrings | 10 +++++ .../Services/PreviewPreinstalling.swift | 41 +++++++++++++++++++ .../View Models/ContentViewModel.swift | 10 +++-- Apps/Attendi/Sources/Views/ContentView.swift | 31 +------------- .../Tests/Mocks/PreinstallingMock.swift | 6 +++ Attendi.xcodeproj/project.pbxproj | 3 ++ 6 files changed, 68 insertions(+), 33 deletions(-) create mode 100644 Apps/Attendi/Previews/Services/PreviewPreinstalling.swift diff --git a/Apps/Attendi/Catalogs/Localizable.xcstrings b/Apps/Attendi/Catalogs/Localizable.xcstrings index b168172..d11fba0 100644 --- a/Apps/Attendi/Catalogs/Localizable.xcstrings +++ b/Apps/Attendi/Catalogs/Localizable.xcstrings @@ -2,6 +2,8 @@ "sourceLanguage" : "en", "strings" : { "view.recording.navigation.title" : { + "comment" : "The title of the navigation bar in the recording view.", + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -60,6 +62,9 @@ } }, "view.recording.picker.locale.title" : { + "comment" : "A label for the picker that selects the locale of the spoken language to transcribe.", + "extractionState" : "manual", + "isCommentAutoGenerated" : true, "localizations" : { "en" : { "stringUnit" : { @@ -70,6 +75,7 @@ } }, "view.transcription.navigation.title" : { + "comment" : "The title of the navigation bar in the transcription modal view.", "extractionState" : "manual", "localizations" : { "en" : { @@ -81,6 +87,8 @@ } }, "view.transcription.unavailable.description" : { + "comment" : "The description of the content unavailable view shown in the transcription modal view for an empty transcription.", + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -91,6 +99,8 @@ } }, "view.transcription.unavailable.title" : { + "comment" : "The title of the content unavailable view shown in the transcription modal view for an empty transcription.", + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { diff --git a/Apps/Attendi/Previews/Services/PreviewPreinstalling.swift b/Apps/Attendi/Previews/Services/PreviewPreinstalling.swift new file mode 100644 index 0000000..16a9ff0 --- /dev/null +++ b/Apps/Attendi/Previews/Services/PreviewPreinstalling.swift @@ -0,0 +1,41 @@ +import Foundation +import Recording + +/// The preinstalling service used by the previews, which resolves every locale as supported and downloads nothing. +struct PreviewPreinstalling: Preinstalling { + + /// The stream of events the service emits, which finishes immediately. + let events: AsyncStream = .init { continuation in + continuation.finish() + } + + /// Simulates the preinstallation of the speech model assets of the given locale, downloading nothing. + /// + /// - Parameter locale: The locale to preinstall the speech model assets for. + func preinstall( + for locale: Locale + ) async { + // The previews preinstall no speech model assets. + } + + /// Returns the given locale as its own supported equivalent. + /// + /// - Parameter locale: The locale to find a supported equivalent for. + /// - Returns: The given locale, unchanged. + func supportedLocale( + equivalentTo locale: Locale + ) async -> Locale? { + locale + } + + /// Returns a sample of supported locales for the locale picker. + /// + /// - Returns: A pair of sample locales. + func supportedLocales() async -> [Locale] { + [ + Locale(identifier: "en-US"), + Locale(identifier: "nl-NL"), + ] + } + +} diff --git a/Apps/Attendi/Sources/View Models/ContentViewModel.swift b/Apps/Attendi/Sources/View Models/ContentViewModel.swift index e9cc6a4..b2e7dc2 100644 --- a/Apps/Attendi/Sources/View Models/ContentViewModel.swift +++ b/Apps/Attendi/Sources/View Models/ContentViewModel.swift @@ -15,6 +15,9 @@ extension ContentView { /// Every picked locale kicks off ``preinstall()``, a cancellable task that preinstalls the locale's speech model assets through the /// preinstalling service — cancelling any download still in flight for a previously picked locale. The service's started, cancelled, and /// failed download events are mapped to the transient, self-dismissing notifications of the ``notifier``. + /// + /// Every collaborator is injected at initialization behind its protocol, defaulting to the on-device services: the app runs the model + /// against the real backends, while its unit tests and previews attach mocks and dummies without touching this type. @MainActor @Observable final class Model { @@ -84,9 +87,10 @@ extension ContentView { // MARK: Methods - /// Loads the locales the transcriber supports into ``locales``, sorted by their localized names, and aligns ``locale`` with the - /// supported equivalent of its current value — falling back to the supported equivalent of a default locale when none exists — - /// so the locale picker starts with a valid selection. + /// Loads the locales the transcriber supports — resolved through the preinstalling service — into ``locales``, sorted by their + /// localized names, and aligns ``locale`` with the supported equivalent of its current value — falling back to the supported + /// equivalent of a default locale when none exists — so the locale picker starts with a valid selection. It then kicks off the + /// preinstallation of the aligned locale's speech model assets. func load() async { locales = await preinstaller diff --git a/Apps/Attendi/Sources/Views/ContentView.swift b/Apps/Attendi/Sources/Views/ContentView.swift index a966888..e02c0da 100644 --- a/Apps/Attendi/Sources/Views/ContentView.swift +++ b/Apps/Attendi/Sources/Views/ContentView.swift @@ -12,7 +12,7 @@ import SwiftUI /// /// The services attached to the feature, the transcription shown in the sheet, and the notifier owning the notifications live in the view's /// ``Model``; the view itself only renders it and forwards the feature's output, the picker's changes, and the sheet's dismissal. All -/// user-facing text is localized through the app's string catalog. +/// user-facing text is localized through the app's string catalog, resolved via its generated string symbols. struct ContentView: View { // MARK: Properties @@ -220,35 +220,6 @@ private enum Constant { // MARK: - Previews -/// The preinstalling service used by the previews, which resolves every locale as supported and downloads nothing. -private struct PreviewPreinstalling: Preinstalling { - - /// The stream of events the service emits, which finishes immediately. - let events: AsyncStream = .init { continuation in - continuation.finish() - } - - func preinstall( - for locale: Locale - ) async { - // The previews preinstall no speech model assets. - } - - func supportedLocale( - equivalentTo locale: Locale - ) async -> Locale? { - locale - } - - func supportedLocales() async -> [Locale] { - [ - Locale(identifier: "en-US"), - Locale(identifier: "nl-NL"), - ] - } - -} - #Preview( "Content view" ) { diff --git a/Apps/Attendi/Tests/Mocks/PreinstallingMock.swift b/Apps/Attendi/Tests/Mocks/PreinstallingMock.swift index a5da06f..ca79172 100644 --- a/Apps/Attendi/Tests/Mocks/PreinstallingMock.swift +++ b/Apps/Attendi/Tests/Mocks/PreinstallingMock.swift @@ -5,6 +5,8 @@ import Recording /// on demand. @MainActor final class PreinstallingMock: Preinstalling { + + // MARK: Properties /// The stream of events the service emits while preinstalling. let events: AsyncStream @@ -20,10 +22,14 @@ final class PreinstallingMock: Preinstalling { /// The continuation that feeds ``events``. private let continuation: AsyncStream.Continuation + + // MARK: Initializers init() { (events, continuation) = AsyncStream.makeStream(of: PreinstallingEvent.self) } + + // MARK: Methods /// Emits the given event through ``events``. /// diff --git a/Attendi.xcodeproj/project.pbxproj b/Attendi.xcodeproj/project.pbxproj index 61c81aa..6d47f00 100644 --- a/Attendi.xcodeproj/project.pbxproj +++ b/Attendi.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ membershipExceptions = ( Attendi/Catalogs/Assets.xcassets, Attendi/Catalogs/Localizable.xcstrings, + Attendi/Previews/Services/PreviewPreinstalling.swift, Attendi/Sources/App/AttendiApp.swift, "Attendi/Sources/View Models/ContentViewModel.swift", Attendi/Sources/Views/ContentView.swift, @@ -285,6 +286,7 @@ COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_ASSET_PATHS = Apps/Attendi/Previews/Services/PreviewPreinstalling.swift; DEVELOPMENT_TEAM = 7FMNM89WKG; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -401,6 +403,7 @@ COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_ASSET_PATHS = Apps/Attendi/Previews/Services/PreviewPreinstalling.swift; DEVELOPMENT_TEAM = 7FMNM89WKG; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES;