Primordial gives your app two speech workflows. You can transcribe a local audio file that your app
already owns, or create a recording session that captures the microphone and delivers text while the
person is speaking. File transcription uses the installed .voiceInput model. Microphone
recording can begin with Apple speech recognition alone and upgrades to the coordinated local speech
resource when Parakeet is installed.
Create a Primordial Client
Import Primordial and create a PrimordialClient.
import Foundation
import Primordial
let primordial = PrimordialClient()
The client already selects Primordial’s default Parakeet 0.6B multilingual speech to text model.
Check Whether Voice Input Is Available
File transcription requires the installed .voiceInput capability. Microphone recording does
not require that download when Apple speech recognition is available, but checking status lets your app
explain whether enhanced multilingual transcription will be used.
let status = await primordial.ai.status(for: .voiceInput)
print(status.isInstalled)
print(status.isAvailable)
print(status.downloadBytes)
print(status.requiredAvailableBytes)
isInstalled tells you whether Parakeet is already on the device.
isAvailable tells you whether the complete local voice capability can be used in the current environment.
When installation is needed, use downloadBytes and requiredAvailableBytes to explain
the download and storage requirement in your own interface before continuing. Checking status does not
load or download the model.
Activate Primordial
Activate Primordial before installing the speech model, transcribing audio, or starting a recording.
try await primordial.activate(
.evaluationKey("pk_eval_your_key_here")
)
You can check primordial.isActivated when your UI needs to reflect activation state.
Prepare Voice Input
makeAvailable checks storage,
downloads the missing speech resources, verifies them, and reports progress as an asynchronous stream.
for try await progress in primordial.ai.makeAvailable(.voiceInput) {
if let fraction = progress.fractionCompleted {
print("Voice setup: \(Int(fraction * 100))%")
}
print(progress.phase)
print(progress.downloadedBytes as Any)
print(progress.totalBytes as Any)
}
Calling makeAvailable again reuses an existing valid download. File transcription loads the
installed model when needed. Recording starts with Apple speech recognition when the model is absent and
upgrades when an installed model is available; neither workflow starts a download silently.
On iOS, add .primordialBackgroundDownloads() once to your main SwiftUI scene. See Background downloads. If the user force-quits your app,
the download is canceled.
Add Speech Permissions
Before recording, add privacy descriptions that tell people why your app needs the microphone and live speech recognition. Use descriptions that name the feature and explain what the captured speech does.
| Xcode key | Raw Info.plist key | Example description |
|---|---|---|
| Privacy - Microphone Usage Description | NSMicrophoneUsageDescription |
Record your voice so the app can create a transcript. |
| Privacy - Speech Recognition Usage Description | NSSpeechRecognitionUsageDescription |
Show live text while you speak. |
A sandboxed macOS app must also enable Audio Input under the App Sandbox capability. File transcription does not request microphone access, but the file must still be local and voice input must already be installed and available.
Choose a Speech Workflow
Both workflows return a PrimordialTranscription, but they begin with different audio sources.
| Workflow | Use it when | Entry point |
|---|---|---|
| Transcribe a file | Your app already has a local audio file to turn into text. | transcribe(_:) |
| Record and transcribe | You want to capture the microphone and update your UI while someone speaks. | recording(localeIdentifier:) |
Transcribe an Audio File
Pass a local file URL owned by your application. A remote web URL is not accepted. You can download remote audio into your app’s storage and use Primordial to transcribe it.
let recordingURL = URL.documentsDirectory
.appendingPathComponent("interview.m4a")
let transcription = try await primordial.transcribe(recordingURL)
print(transcription.text)
print(transcription.duration)
print(transcription.languageIdentifier as Any)
for segment in transcription.segments {
print(segment.text, segment.startTime, segment.endTime)
}
text contains the complete transcription and duration contains the audio duration in
seconds. When the selected speech engine reports them, segments divide the transcription into
timed pieces and languageIdentifier identifies the detected language. The language identifier
may be nil, and some results may not contain timed segments.
Record and Transcribe Speech
The Parakeet 0.6B v3 multilingual ASR model does not support real-time transcription. Primordial SDK provides
dual-track transcription by using
SFSpeechRecognizer
for fast provisional text while Parakeet produces the stable final transcript.
SFSpeechRecognizer is Apple’s built-in speech recognition framework.
It
is fast and responsive, but not multilingual or accurate as Parakeet. By default, Primordial uses
en-US,
but you can choose another locale when creating the recorder with localeIdentifier.
When Parakeet is not installed, recording remains available as a single-track Apple speech session.
liveText provides the provisional transcript and is promoted to the final result when the
recording stops. If Parakeet is installed, Primordial uses dual-track recording and
isDualTrack becomes true. Starting a recording never downloads Parakeet.
let recording = primordial.recording(
localeIdentifier: "en-US"
)
try await recording.start { update in
showLiveText(update.liveText)
showConfirmedText(update.confirmedText)
showRecordingState(update.state)
}
start asks for microphone and speech-recognition permission at the point of use. The update
closure runs on the main actor, so it can update interface state directly. Primordial manages the update
stream and its task lifetime for the application-level workflow.
Understand recording updates
| Value | Meaning |
|---|---|
liveText |
Fast, provisional text that can change while the person continues speaking. |
confirmedText |
Text confirmed by Primordial’s local speech transcription path. |
isDualTrack |
true when both live and confirmed transcription tracks are active. |
isFinal |
true for the final confirmed update delivered when recording stops. |
state |
The current idle, recording, paused, stopping, or ended state. |
We recommend clearly indicating that the liveText is not multi-lingual, provisional and may change.
Only treat it as temporary interface feedback. In the UI, show the live transcript in a different color or style to distinguish it from the final transcript.
Pause, Resume, Stop, or Cancel the Recording
Pause without ending the session when capture is temporarily interrupted. A paused session keeps its speech resource ready, preserves confirmed text and captured audio, and can be stopped or resumed.
try await recording.pause()
// Resume the same transcript and audio result later.
try await recording.resume()
Paused time is excluded from transcription.duration, and pause gaps are not encoded as
silence. Calling pause or resume from an incompatible state throws an error.
Call stop() when the person finishes speaking. It ends microphone capture, delivers the final
update, and returns the completed multi-lingual transcription with any encoded recording data that is available.
let result = try await recording.stop()
showTranscript(result.transcription.text)
if let audioData = result.audioData {
saveRecording(audioData)
}
audioData is optional because a session can finish without captured audio or audio conversion
may fail even though transcription succeeds. The returned transcription includes the requested locale
identifier and elapsed recording duration, timed segments may be empty for microphone recordings.
When the person abandons the recording, cancel it explicitly:
await recording.cancel()
Pausing retains the active speech resource. Stopping and canceling release it. A recording session is one-shot: after
stop() or cancel(), create a new session before recording again.
Handle Speech Errors
Speech operations fail explicitly when activation is required, permission is denied, the audio URL is
invalid, another resource operation conflicts with active speech, or transcription or recording fails.
File transcription also fails when voice input is not installed. Microphone recording can use Apple
speech recognition without that installation. Swift task cancellation remains CancellationError.
do {
let transcription = try await primordial.transcribe(recordingURL)
showTranscript(transcription.text)
} catch is CancellationError {
print("Transcription canceled")
} catch let error as PrimordialActivationError {
print(error.localizedDescription)
} catch let error as PrimordialAIError {
switch error {
case .capabilityUnavailable(let requirement):
print("Voice download: \(requirement.downloadBytes) bytes")
case .permissionDenied(let message),
.invalidRecord(let message),
.resourceConflict(let message):
print(message)
case .transcriptionFailed:
print("The audio could not be transcribed")
case .recordingFailed:
print("The recording could not be completed")
default:
print(error.localizedDescription)
}
}
Do not start a model download automatically from an error handler. Explain what is missing, ask the
person first, and call makeAvailable(.voiceInput) only after they agree.
For permission recovery, simulator limitations, and interrupted downloads, see Recording or transcription fails.