SwiftSpeech

repository·master·Indexed 19 days ago

https://github.com/cay-zhang/swiftspeech

A SwiftUI and Combine-powered wrapper for Apple's Speech framework. It simplifies speech recognition by handling authorization, audio engines, and reactive data streams through a composable structure of View Components, Functional Components, and modifiers. It supports recording gestures like hold-to-record and tap-to-toggle, as well as independent session management via SwiftSpeech.Session.

Tokens
2.7K
Snippets
9
Records
10
Agent score
18%

What's inside SwiftSpeech

  1. Understand the SwiftSpeech Composable Structure

    master

    SwiftSpeech is built using a three-part composable structure that separates UI, interaction logic, and data handling:

    1. The View Component: A View responsible solely for the UI (e.g., SwiftSpeech.RecordButton()).
    2. The Functional Component: Handles user interaction and provides speech recognition functionality. It is applied as a modifier to the View Component. Examples include:
      • .swiftSpeechRecordOnHold(sessionConfiguration:animation:distanceToCancel:): Enables recording while holding and allows canceling by swiping up.
      • .swiftSpeechToggleRecordingOnTap(sessionConfiguration:animation:): Toggles recording on tap.
    3. SwiftSpeech Modifier(s): Applied to the functional component to receive and manipulate recognition results (e.g., .onRecognizeLatest(...)).

    Note on Chaining: Chaining multiple or identical modifiers does not override behavior; all actions execute in order, starting from the modifier closest to the Functional Component.

    SwiftSpeech.RecordButton()                                        // 1. The View Component
        .swiftSpeechRecordOnHold(sessionConfiguration:animation:distanceToCancel:)  // 2. The Functional Component
        .onRecognizeLatest(update: $text)                             // 3. SwiftSpeech Modifier(s)
  2. Understand the View Component vs. Functional Component model

    master

    SwiftSpeech uses a decoupled architecture for UI development:

    1. View Components: These are dedicated View objects used for design. They do not handle user interaction or state directly. Instead, they react to their environment using specific environment variables. This makes them highly composable.
    2. Functional Components: These are components that handle user interactions (gestures) and trigger the actual speech recognition logic.

    To build a custom UI, you use a View Component to define the look and a Functional Component (or a modifier) to define the behavior.

  3. Install SwiftSpeech via SPM or CocoaPods

    master

    You can install SwiftSpeech using either Swift Package Manager (recommended) or CocoaPods.

    Swift Package Manager In Xcode, select Add Packages... from the File menu and enter the following URL: https://github.com/Cay-Zhang/SwiftSpeech

    CocoaPods Add the following to your Podfile:

    pod 'SwiftSpeech'
  4. Configure Authorization for Speech Recognition

    master

    SwiftSpeech handles the verbose authorization logic, but you must provide usage descriptions in your Info.plist and trigger the request manually.

    1. Add Usage Descriptions to Info.plist

    You must add the following keys to your Info.plist to explain to the user why the app needs access to speech recognition and the microphone:

    • NSSpeechRecognitionUsageDescription
    • NSMicrophoneUsageDescription

    Example XML:

    <key>NSSpeechRecognitionUsageDescription</key>
    <string>This app uses speech recognition to convert your speech into text.</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>This app uses the mircrophone to record audio for speech recognition.</string>

    2. Request Authorization

    Call SwiftSpeech.requestSpeechRecognitionAuthorization() to trigger the permission prompt. A common pattern is to call this within an .onAppear modifier:

    .onAppear {
        SwiftSpeech.requestSpeechRecognitionAuthorization()
    }
  5. Use SwiftSpeech Modifiers to handle recognition results

    master

    SwiftSpeech provides several modifiers to handle the stream of recognition data. Modifiers can be grouped by their purpose:

    Result Handling Modifiers

    These handle the actual text and error data.

    • .onRecognizeLatest(includePartialResults:handleResult:handleError:): Subscribes to results from the most recent recording session, ignoring results from previous sessions when a new one starts.
    • .onRecognize(includePartialResults:handleResult:handleError:): Subscribes to results from every recording session.
    • .onRecognizeLatest(includePartialResults:update:): A convenience modifier that assigns recognized text directly to a Binding<String>.
    • .printRecognizedText(includePartialResults:): A debugging modifier that prints recognized text to the console.

    In the handleResult closure, you receive a SwiftSpeech.Session (to identify the specific recording via its id) and an SFSpeechRecognitionResult (which contains the bestTranscription.formattedString, speaking rate, and pitch).

    Lifecycle Modifiers

    These allow you to execute code at specific points in a session's lifecycle:

    • .onStartRecording(appendAction:)
    • .onStopRecording(appendAction:)
    • .onCancelRecording(appendAction:)

    Reactive (Combine) Modifiers

    If you prefer a reactive style, these modifiers send the SwiftSpeech.Session to a Combine.Subject (like PassthroughSubject or CurrentValueSubject):

    • .onStartRecording(sendSessionTo:)
    • .onStopRecording(sendSessionTo:)
    • .onCancelRecording(sendSessionTo:)
    // Example of result handling
    .onRecognizeLatest(update: $text)
    
    // Example of lifecycle handling
    .onStartRecording(appendAction: { session in
        print("Started session: \(session.id)")
    })
  6. Use SwiftSpeech.Session for independent speech recognition

    master

    If you do not want to use the built-in SwiftUI components, you can use SwiftSpeech.Session directly to manage recognition.

    Configuration

    A session is initialized with a SwiftSpeech.Session.Configuration object, which allows you to specify:

    • locale: The Locale for recognition.
    • contextualStrings: Custom phrases to help recognition.
    • taskHint: The type of speech task.
    • options: On-device recognition settings and audio session configurations.

    Subscribing to Results

    A Session publishes results via two main publishers:

    1. resultPublisher: Emits SFSpeechRecognitionResult and can fail with an Error. It completes when the session is finished, an error occurs, or cancelRecording() is called.
    2. stringPublisher: A convenience publisher that maps results directly to the recognized string.

    Example: Manual Session Usage

    let configuration = SwiftSpeech.Session.Configuration(locale: Locale(identifier: "en-US"), contextualStrings: ["SwiftSpeech"])
    let session = SwiftSpeech.Session(configuration: configuration)
    
    try session.startRecording()
    
    session.stringPublisher?
        .sink { text in
            print("Recognized: \(text)")
        }
        .store(in: &cancelBag)
    let session = SwiftSpeech.Session(configuration: SwiftSpeech.Session.Configuration(locale: Locale(identifier: "en-US"), contextualStrings: ["SwiftSpeech"]))
    try session.startRecording()
    session.stringPublisher?
        .sink { text in
            // do something with the text
        }
        .store(in: &cancelBag)
  7. Use Environment variables to build custom View Components

    master

    When creating a custom View Component, you can access the current recording state and authorization status via the SwiftUI Environment. This allows your view to react visually to changes without managing the underlying logic.

    • @Environment(\.swiftSpeechState) var state: Provides the current SwiftSpeech.State.
    • @SpeechRecognitionAuthStatus var authStatus: Provides the SFSpeechRecognizerAuthorizationStatus (use $authStatus as a shorthand for authStatus == .authorized).
    @Environment(\.swiftSpeechState) var state: SwiftSpeech.State
    @SpeechRecognitionAuthStatus var authStatus
  8. Apply SwiftSpeech Modifiers for recording gestures

    master

    SwiftSpeech provides functional modifiers that you can attach to any view to implement common speech recognition gestures. These modifiers handle the gesture and the speech recognition session for you.

    swiftSpeechRecordOnHold

    Adds a 'hold' gesture to the view. When held, recording starts; when released, it stops.

    • sessionConfiguration: SwiftSpeech.Session.Configuration (defaults to default).
    • animation: Animation (defaults to SwiftSpeech.defaultAnimation).
    • distanceToCancel: CGFloat (defaults to 50.0).

    swiftSpeechToggleRecordingOnTap

    Adds a 'tap' gesture to the view to toggle recording on and off.

    • sessionConfiguration: SwiftSpeech.Session.Configuration (defaults to default).
    • animation: Animation (defaults to SwiftSpeech.defaultAnimation).
    // Example usage of modifiers
    Text("Hold to Record")
        .swiftSpeechRecordOnHold(
            sessionConfiguration: SwiftSpeech.Session.Configuration(),
            animation: .easeInOut,
            distanceToCancel: 50.0
        )
    
    Text("Tap to Toggle")
        .swiftSpeechToggleRecordingOnTap()
  9. Implement custom gestures with FunctionalComponentDelegate

    master

    If you need to implement a custom gesture (other than tap or hold) for speech recognition, you can create a custom functional component by using a SwiftSpeech.FunctionalComponentDelegate. You would then call the delegate's methods at the appropriate times during your custom gesture lifecycle.

    var delegate = SwiftSpeech.FunctionalComponentDelegate()
  10. Reference SwiftSpeech.State enum values

    master

    The SwiftSpeech.State enum represents the lifecycle of a recording session. It is available via the @Environment(\.swiftSpeechState) key.

    enum SwiftSpeech.State {
        /// Indicating there is no recording in progress.
        case pending
        /// Indicating there is a recording in progress and the user does not intend to cancel it.
        case recording
        /// Indicating there is a recording in progress and the user intends to cancel it.
        case cancelling
    }