import Foundation import Observation import Recording import Speech extension ContentView { /// The observable model that drives ``ContentView``. /// /// The model owns the recording and transcribing services attached to the recording feature, along with the ``locale`` the feature /// transcribes in, picked in the view's toolbar from the supported ``locales`` that ``load()`` fetches. It also holds the transcription /// currently presented in the view's modal sheet: ``received(_:)`` presents the transcription of a processed recording, and /// ``dismissed()`` clears it when the sheet closes. @MainActor @Observable final class Model { // MARK: Properties /// The locale of the spoken language to transcribe. var locale: Locale /// The transcription currently presented in the modal sheet, or `nil` when none is shown. var transcription: Transcription? /// The locales the transcriber supports, sorted by their localized names, or empty while ``load()`` has not finished yet. private(set) var locales: [Locale] /// The service that captures the audio from the device's microphone. @ObservationIgnored let recorder: AudioRecordingService /// The service that transcribes the recorded audio into text on device. @ObservationIgnored let transcriber: AudioTranscribing // MARK: Initializers /// Creates a model set to the user's current locale, with no supported locales loaded yet, attached to the on-device recording /// and transcribing services. init() { self.locale = .current self.locales = [] self.recorder = .init() self.transcriber = .init() } // 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 so the locale picker starts with a valid selection. func load() async { locales = await SpeechTranscriber.supportedLocales.sorted { name(for: $0).localizedStandardCompare(name(for: $1)) == .orderedAscending } } /// Returns the localized name of a locale for the locale picker. /// /// - Parameter locale: The locale to name. /// - Returns: The name of the locale in the user's current locale, or its identifier when no name is available. func name( for locale: Locale ) -> String { Locale.current .localizedString( forIdentifier: locale.identifier )?.localizedCapitalized ?? locale.identifier } /// Handles the transcription of a processed recording, presenting it in the modal sheet. /// /// - Parameter transcription: The transcription of the processed recording. func received( _ transcription: Transcription ) { self.transcription = transcription } /// Handles the dismissal of the modal sheet. func dismissed() { transcription = nil } } }