I am trying to record audio from a record button inside Keyboards View and then play it back to the user with the play button but neither its recording nor it is playing the audio.
Xcode Version: 13.2.1
Here is the error:
Screenshot of Error when the record button is tapped
Below is my code for recording and playing the audio:
class AudioRecoder: NSObject, AVAudioRecorderDelegate, AVAudioPlayerDelegate {
var recordingSession: AVAudioSession!
var audioRecorder: AVAudioRecorder!
var audioPlayer = AVAudioPlayer()
func askPermission() {
recordingSession = AVAudioSession.sharedInstance()
do {
try recordingSession.setCategory(.playAndRecord, mode: .default)
try recordingSession.setActive(true)
recordingSession.requestRecordPermission() { [unowned self] allowed in
DispatchQueue.main.async {
if allowed {
print("user has allowed")
} else {
print("user has not allowed")
}
}
}
} catch {
// failed to record!
print("failed to record!")
}
}
func playSound() throws {
let url = getDocumentsDirectory().appendingPathComponent("recording.m4a")
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer.prepareToPlay()
audioPlayer.play()
}
func startRecording() {
let audioFilename = getDocumentsDirectory().appendingPathComponent("recording.m4a")
let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
do {
audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
audioRecorder.delegate = self
audioRecorder.record()
//recordButton.setTitle("Tap to Stop", for: .normal)
} catch {
finishRecording(success: false)
}
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
func finishRecording(success: Bool) {
audioRecorder.stop()
audioRecorder = nil
if success {
//recordButton.setTitle("Tap to Re-record", for: .normal)
} else {
//recordButton.setTitle("Tap to Record", for: .normal)
// recording failed :(
}
}
func recordTapped() {
if audioRecorder == nil {
startRecording()
} else {
finishRecording(success: true)
}
}
func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
if !flag {
finishRecording(success: false)
}
}}
2
Answers
Your code is working correctly actually. I just put your code into an example project and it worked fine.
Make sure you added Microphone Usage Description to Info.plist and I would suggest putting some logs in the code to understand your issue. For example:
Also you can download the container and check in the Documents directory whether it is actually saving the audio file.
As per Apple’s documentation, custom keyboards do not have no access to microphone and speaker. You cannot directly use microphone from your keybaord extension app.
There is a workaround, you can initiate audio recording from your containing app and pass the speech-to-text ‘text’ to the keyboard extension.
Any extension app will have these 3 components.
So basically, when you click on the MIC button on custom keybaord, open the containing app with URL Scheme, start recording, come back to host app (recording will be ON in backgorund) and dictate. The dication will be processed in containing app and passed/shared to keyboard extension.
Download the Google’s GBoard keybaord or Microsoft’s SwiftKey keyboard from App Store to see it working.