Here is my code and I tried to list stored data from Core Data in WidgetKit its not showing at all. I already created app group and the data are showing at preview but when we add widget to home screen nothing shows up. I'm not sure the way I did is correct or not.
What is the best way to list Core Data records in WidgetKit?
import WidgetKit
import SwiftUI
import CoreData
// MARK: For Core Data
public extension URL {
/// Returns a URL for the given app group and database pointing to the sqlite database.
static func storeURL(for appGroup: String, databaseName: String) -> URL {
guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else {
fatalError("Shared file container could not be created.")
}
return fileContainer.appendingPathComponent("\(databaseName).sqlite")
}
}
var managedObjectContext: NSManagedObjectContext {
return persistentContainer.viewContext
}
var workingContext: NSManagedObjectContext {
let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
context.parent = managedObjectContext
return context
}
var persistentContainer: NSPersistentCloudKitContainer = {
let container = NSPersistentCloudKitContainer(name: "Countdowns")
let storeURL = URL.storeURL(for: "group.app-group-countdowns", databaseName: "Countdowns")
let description = NSPersistentStoreDescription(url: storeURL)
container.loadPersistentStores(completionHandler: { storeDescription, error in
if let error = error as NSError? {
print(error)
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
return container
}()
// MARK: For Widget
struct Provider: TimelineProvider {
var moc = managedObjectContext
init(context : NSManagedObjectContext) {
self.moc = context
}
func placeholder(in context: Context) -> SimpleEntry {
return SimpleEntry(date: Date())
}
func getSnapshot(in context: Context, completion: #escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date())
return completion(entry)
}
func getTimeline(in context: Context, completion: #escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
let currentDate = Date()
let entryDate = Calendar.current.date(byAdding: .minute, value: 1, to: currentDate)!
let entry = SimpleEntry(date: entryDate)
entries.append(entry)
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
}
struct CountdownsWidgetEntryView : View {
var entry: Provider.Entry
#FetchRequest(entity: Countdown.entity(), sortDescriptors: []) var countdowns: FetchedResults<Countdown>
var body: some View {
return (
VStack {
ForEach(countdowns, id: \.self) { (memoryItem: Countdown) in
Text(memoryItem.title ?? "Default title")
}.environment(\.managedObjectContext, managedObjectContext)
Text(entry.date, style: .time)
}
)
}
}
#main
struct CountdownsWidget: Widget {
let kind: String = "CountdownsWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider(context: managedObjectContext)) { entry in
CountdownsWidgetEntryView(entry: entry)
.environment(\.managedObjectContext, managedObjectContext)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
struct CountdownsWidget_Previews: PreviewProvider {
static var previews: some View {
CountdownsWidgetEntryView(entry: SimpleEntry(date: Date()))
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
Just call execute on the managed object context & since you are using a private context, make sure you call performBlock.
NSManagedObjectContext* moc = [self getManagedObjectContext];
[moc performBlock:^{
NSString *entityName = #"myEntity";
NSFetchRequest *fRequest = [[NSFetchRequest alloc]initWithEntityName:entityName];
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"SELECT * FROM myEntity"];
[fRequest setPredicate:predicate];
NSError *error = nil;
NSArray *results = [moc executeFetchRequest:fRequest error:&error];
}];
I just want to write a struct type array into a file, but after I write it, the file is not created without any error message!!!???
The code :
struct temp {
var a : String = ""
var b : Date = Date()
init(
a : String = "",
b : Date = Date(),
) {
self.a = ""
self.b = Date()
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var b = [temp]()
var c = temp()
c.a = "John"
c.b = Date()
b.append(c)
c.a = "Sally"
b.append(c)
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = dir.appendingPathComponent("testFile")
do{
(b as NSArray).write(to: fileURL, atomically: true)
}catch{
print(error)
}
}
getTheFile()
}
func getTheFile() {
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = dir.appendingPathComponent("testFile")
do {
print(try String(contentsOf: fileURL))
}catch{
print("read error:")
print(error)
}
}
}
It has a error message in getTheFile()
read error:
Error Domain=NSCocoaErrorDomain Code=260 "The file “ testFile” couldn’t be opened because there is no such file.
You cannot write custom structs to disk. You have to serialize them.
The easist way is the Codable protocol and PropertyListEncoder/Decoder
struct Temp : Codable { // the init method is redundant
var a = ""
var b = Date()
}
var documentsDirectory : URL {
return try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var b = [Temp]()
var c = Temp()
c.a = "John"
c.b = Date()
b.append(c)
c.a = "Sally"
b.append(c)
let fileURL = documentsDirectory.appendingPathComponent("testFile.plist")
do {
let data = try PropertyListEncoder().encode(b)
try data.write(to: fileURL)
getTheFile()
} catch {
print(error)
}
}
func getTheFile() {
let fileURL = documentsDirectory.appendingPathComponent("testFile.plist")
do {
let data = try Data(contentsOf: fileURL)
let temp = try PropertyListDecoder().decode([Temp].self, from: data)
print(temp)
} catch {
print("read error:", error)
}
}
Note:
In Swift never use the NSArray/NSDictionary APIs to read and write property lists. Use either Codable or PropertyListSerialization.
If the array's contents are all property list objects (like: NSString, NSData, NSArray, NSDictionary) then only you can use writeToFile method to write the array in a file.
Here in your implementation, the array contains structure which is a value type, not an object. This is why you are getting an error.
I am trying to re-use a code from swift 7.3. I feel I am very close.
When I try the code on the simulator, the picker view it is empty, but when I try to use it I have only one data show up.
I also try to adapt and other code to load data from core data to it, didn't work.
Any Idea?
Thank you
import CoreData
class ViewController: UIViewController, UIPickerViewDelegate, UIImagePickerControllerDelegate {
#IBOutlet weak var TextField: UITextField!
#IBOutlet weak var stylePicker: UIPickerView!
var styleData = [PilotBase]()
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PilotBase")
do {
styleData = try context.fetch(fetchRequest) as! [PilotBase]
} catch let err as NSError {
print(err.debugDescription)
}
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "PilotBase")
request.returnsObjectsAsFaults = false
do{
let result = try context.fetch(request)
for data in result as! [NSManagedObject]{
print(data.value(forKey: "name") as! String)
}
}catch{
print("Failed")
}
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == 0 {
return styleData.count
} else {
return 0
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if component == 0 {
return styleData[row].name
} else {
return nil
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
print(styleData[row])
stylePicker.selectRow(row, inComponent: 0, animated: true)
TextField.text = styleData[row].name
}
}
Below the code to add data to Core Data.
#IBAction func Save(_ sender: Any) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "PilotBase", in: context)
let newUser = NSManagedObject(entity: entity!, insertInto: context)
newUser.setValue(TextField.text!, forKey: "name")
do{
try context.save()
}catch{
print("failed saving")
}
}
Does anyone know where I can find info on how to record audio in a Swift application? I've been looking at some of the audio playback examples but I can't seem to be able to find anything on implementing the audio recording. Thanks
In Swift 3
Add framework AVFoundation
**In info.plist add key value
Key = Privacy - Microphone Usage Description and Value = For using
microphone
(the apps will crash if you don't provide the value - description why you are asking for the permission)**
Import AVFoundation & AVAudioRecorderDelegate, AVAudioPlayerDelegate
import AVFoundation
class RecordVC: UIViewController , AVAudioRecorderDelegate, AVAudioPlayerDelegate
Create button for record audio & play audio , and label for display recording timing & give outlets and action as start_recording , play_recording & declare some variables which we will use later
#IBOutlet var recordingTimeLabel: UILabel!
#IBOutlet var record_btn_ref: UIButton!
#IBOutlet var play_btn_ref: UIButton!
var audioRecorder: AVAudioRecorder!
var audioPlayer : AVAudioPlayer!
var meterTimer:Timer!
var isAudioRecordingGranted: Bool!
var isRecording = false
var isPlaying = false
In viewDidLoad check record permission
override func viewDidLoad() {
super.viewDidLoad()
check_record_permission()
}
func check_record_permission()
{
switch AVAudioSession.sharedInstance().recordPermission() {
case AVAudioSessionRecordPermission.granted:
isAudioRecordingGranted = true
break
case AVAudioSessionRecordPermission.denied:
isAudioRecordingGranted = false
break
case AVAudioSessionRecordPermission.undetermined:
AVAudioSession.sharedInstance().requestRecordPermission({ (allowed) in
if allowed {
self.isAudioRecordingGranted = true
} else {
self.isAudioRecordingGranted = false
}
})
break
default:
break
}
}
generate path where you want to save that recording as myRecording.m4a
func getDocumentsDirectory() -> URL
{
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
func getFileUrl() -> URL
{
let filename = "myRecording.m4a"
let filePath = getDocumentsDirectory().appendingPathComponent(filename)
return filePath
}
Setup the recorder
func setup_recorder()
{
if isAudioRecordingGranted
{
let session = AVAudioSession.sharedInstance()
do
{
try session.setCategory(AVAudioSessionCategoryPlayAndRecord, with: .defaultToSpeaker)
try session.setActive(true)
let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2,
AVEncoderAudioQualityKey:AVAudioQuality.high.rawValue
]
audioRecorder = try AVAudioRecorder(url: getFileUrl(), settings: settings)
audioRecorder.delegate = self
audioRecorder.isMeteringEnabled = true
audioRecorder.prepareToRecord()
}
catch let error {
display_alert(msg_title: "Error", msg_desc: error.localizedDescription, action_title: "OK")
}
}
else
{
display_alert(msg_title: "Error", msg_desc: "Don't have access to use your microphone.", action_title: "OK")
}
}
Start recording when button start_recording press & display seconds using updateAudioMeter, & if recording is start then finish the recording
#IBAction func start_recording(_ sender: UIButton)
{
if(isRecording)
{
finishAudioRecording(success: true)
record_btn_ref.setTitle("Record", for: .normal)
play_btn_ref.isEnabled = true
isRecording = false
}
else
{
setup_recorder()
audioRecorder.record()
meterTimer = Timer.scheduledTimer(timeInterval: 0.1, target:self, selector:#selector(self.updateAudioMeter(timer:)), userInfo:nil, repeats:true)
record_btn_ref.setTitle("Stop", for: .normal)
play_btn_ref.isEnabled = false
isRecording = true
}
}
func updateAudioMeter(timer: Timer)
{
if audioRecorder.isRecording
{
let hr = Int((audioRecorder.currentTime / 60) / 60)
let min = Int(audioRecorder.currentTime / 60)
let sec = Int(audioRecorder.currentTime.truncatingRemainder(dividingBy: 60))
let totalTimeString = String(format: "%02d:%02d:%02d", hr, min, sec)
recordingTimeLabel.text = totalTimeString
audioRecorder.updateMeters()
}
}
func finishAudioRecording(success: Bool)
{
if success
{
audioRecorder.stop()
audioRecorder = nil
meterTimer.invalidate()
print("recorded successfully.")
}
else
{
display_alert(msg_title: "Error", msg_desc: "Recording failed.", action_title: "OK")
}
}
Play the recording
func prepare_play()
{
do
{
audioPlayer = try AVAudioPlayer(contentsOf: getFileUrl())
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
}
catch{
print("Error")
}
}
#IBAction func play_recording(_ sender: Any)
{
if(isPlaying)
{
audioPlayer.stop()
record_btn_ref.isEnabled = true
play_btn_ref.setTitle("Play", for: .normal)
isPlaying = false
}
else
{
if FileManager.default.fileExists(atPath: getFileUrl().path)
{
record_btn_ref.isEnabled = false
play_btn_ref.setTitle("pause", for: .normal)
prepare_play()
audioPlayer.play()
isPlaying = true
}
else
{
display_alert(msg_title: "Error", msg_desc: "Audio file is missing.", action_title: "OK")
}
}
}
When recording is finish enable the play button & when play is finish enable the record button
func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool)
{
if !flag
{
finishAudioRecording(success: false)
}
play_btn_ref.isEnabled = true
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool)
{
record_btn_ref.isEnabled = true
}
Generalize function for display alert
func display_alert(msg_title : String , msg_desc : String ,action_title : String)
{
let ac = UIAlertController(title: msg_title, message: msg_desc, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: action_title, style: .default)
{
(result : UIAlertAction) -> Void in
_ = self.navigationController?.popViewController(animated: true)
})
present(ac, animated: true)
}
Here is code.You can record easily.Write this code on IBAction.It will save the recording in Documents by name recordTest.caf
//declare instance variable
var audioRecorder:AVAudioRecorder!
func record(){
var audioSession:AVAudioSession = AVAudioSession.sharedInstance()
audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, error: nil)
audioSession.setActive(true, error: nil)
var documents: AnyObject = NSSearchPathForDirectoriesInDomains( NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0]
var str = documents.stringByAppendingPathComponent("recordTest.caf")
var url = NSURL.fileURLWithPath(str as String)
var recordSettings = [AVFormatIDKey:kAudioFormatAppleIMA4,
AVSampleRateKey:44100.0,
AVNumberOfChannelsKey:2,AVEncoderBitRateKey:12800,
AVLinearPCMBitDepthKey:16,
AVEncoderAudioQualityKey:AVAudioQuality.Max.rawValue]
println("url : \(url)")
var error: NSError?
audioRecorder = AVAudioRecorder(URL:url, settings: recordSettings, error: &error)
if let e = error {
println(e.localizedDescription)
} else {
audioRecorder.record()
}
}
Swift2 version of #codester's answer.
func record() {
//init
let audioSession:AVAudioSession = AVAudioSession.sharedInstance()
//ask for permission
if (audioSession.respondsToSelector("requestRecordPermission:")) {
AVAudioSession.sharedInstance().requestRecordPermission({(granted: Bool)-> Void in
if granted {
print("granted")
//set category and activate recorder session
try! audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
try! audioSession.setActive(true)
//get documnets directory
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let fullPath = documentsDirectory.stringByAppendingPathComponent("voiceRecording.caf")
let url = NSURL.fileURLWithPath(fullPath)
//create AnyObject of settings
let settings: [String : AnyObject] = [
AVFormatIDKey:Int(kAudioFormatAppleIMA4), //Int required in Swift2
AVSampleRateKey:44100.0,
AVNumberOfChannelsKey:2,
AVEncoderBitRateKey:12800,
AVLinearPCMBitDepthKey:16,
AVEncoderAudioQualityKey:AVAudioQuality.Max.rawValue
]
//record
try! self.audioRecorder = AVAudioRecorder(URL: url, settings: settings)
} else{
print("not granted")
}
})
}
}
In addition to previous answers, I tried to make it work on Xcode 7.2 and I couldn't hear any sound after, neither when I sent the file via email. No warning or exception.
So I changed settings to the following and stored as an .m4a file.
let recordSettings = [AVSampleRateKey : NSNumber(float: Float(44100.0)),
AVFormatIDKey : NSNumber(int: Int32(kAudioFormatMPEG4AAC)),
AVNumberOfChannelsKey : NSNumber(int: 1),
AVEncoderAudioQualityKey : NSNumber(int: Int32(AVAudioQuality.Medium.rawValue))]
After that I could listen to sound.
For saving the file, I added this on viewDidLoad to initialise the recorder:
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
try audioRecorder = AVAudioRecorder(URL: self.directoryURL()!,
settings: recordSettings)
audioRecorder.prepareToRecord()
} catch {
}
And for creating the directory:
func directoryURL() -> NSURL? {
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let documentDirectory = urls[0] as NSURL
let soundURL = documentDirectory.URLByAppendingPathComponent("sound.m4a")
return soundURL
}
I also add the actions used to start recording, stop, and play after
#IBAction func doRecordAction(sender: AnyObject) {
if !audioRecorder.recording {
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setActive(true)
audioRecorder.record()
} catch {
}
}
}
#IBAction func doStopRecordingAction(sender: AnyObject) {
audioRecorder.stop()
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setActive(false)
} catch {
}
}
#IBAction func doPlayAction(sender: AnyObject) {
if (!audioRecorder.recording){
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: audioRecorder.url)
audioPlayer.play()
} catch {
}
}
}
Here audio recorder with simple interface written on Swift 4.2 .
final class AudioRecorderImpl: NSObject {
private let session = AVAudioSession.sharedInstance()
private var player: AVAudioPlayer?
private var recorder: AVAudioRecorder?
private lazy var permissionGranted = false
private lazy var isRecording = false
private lazy var isPlaying = false
private var fileURL: URL?
private let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2,
AVEncoderAudioQualityKey:AVAudioQuality.high.rawValue
]
override init() {
fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("note.m4a")
}
func record(to url: URL?) {
guard permissionGranted,
let url = url ?? fileURL else { return }
setupRecorder(url: url)
if isRecording {
stopRecording()
}
isRecording = true
recorder?.record()
}
func stopRecording() {
isRecording = false
recorder?.stop()
try? session.setActive(false)
}
func play(from url: URL?) {
guard let url = url ?? fileURL else { return }
setupPlayer(url: url)
if isRecording {
stopRecording()
}
if isPlaying {
stopPlaying()
}
if FileManager.default.fileExists(atPath: url.path) {
isPlaying = true
setupPlayer(url: url)
player?.play()
}
}
func stopPlaying() {
player?.stop()
}
func pause() {
player?.pause()
}
func resume() {
if player?.isPlaying == false {
player?.play()
}
}
func checkPermission(completion: ((Bool) -> Void)?) {
func assignAndInvokeCallback(_ granted: Bool) {
self.permissionGranted = granted
completion?(granted)
}
switch session.recordPermission {
case .granted:
assignAndInvokeCallback(true)
case .denied:
assignAndInvokeCallback(false)
case .undetermined:
session.requestRecordPermission(assignAndInvokeCallback)
}
}
}
extension AudioRecorderImpl: AVAudioRecorderDelegate, AVAudioPlayerDelegate {
}
private extension AudioRecorderImpl {
func setupRecorder(url: URL) {
guard
permissionGranted else { return }
try? session.setCategory(.playback, mode: .default)
try? session.setActive(true)
let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
recorder = try? AVAudioRecorder(url: url, settings: settings)
recorder?.delegate = self
recorder?.isMeteringEnabled = true
recorder?.prepareToRecord()
}
func setupPlayer(url: URL) {
player = try? AVAudioPlayer(contentsOf: url)
player?.delegate = self
player?.prepareToPlay()
}
}
For Swift 5,
func setup_recorder()
{
if isAudioRecordingGranted
{
let session = AVAudioSession.sharedInstance()
do
{
try session.setCategory(.playAndRecord, mode: .default)
try session.setActive(true)
let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2,
AVEncoderAudioQualityKey:AVAudioQuality.high.rawValue
]
audioRecorder = try AVAudioRecorder(url: getFileUrl(), settings: settings)
audioRecorder.delegate = self
audioRecorder.isMeteringEnabled = true
audioRecorder.prepareToRecord()
}
catch let error {
display_alert(msg_title: "Error", msg_desc: error.localizedDescription, action_title: "OK")
}
}
else
{
display_alert(msg_title: "Error", msg_desc: "Don't have access to use your microphone.", action_title: "OK")
}
Code in Class file Using Swift 4
Class is AGAudioRecorder
Code is
class AudioRecordViewController: UIViewController {
#IBOutlet weak var recodeBtn: UIButton!
#IBOutlet weak var playBtn: UIButton!
var state: AGAudioRecorderState = .Ready
var recorder: AGAudioRecorder = AGAudioRecorder(withFileName: "TempFile")
override func viewDidLoad() {
super.viewDidLoad()
recodeBtn.setTitle("Recode", for: .normal)
playBtn.setTitle("Play", for: .normal)
recorder.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func recode(_ sender: UIButton) {
recorder.doRecord()
}
#IBAction func play(_ sender: UIButton) {
recorder.doPlay()
}
}
extension AudioRecordViewController: AGAudioRecorderDelegate {
func agAudioRecorder(_ recorder: AGAudioRecorder, withStates state: AGAudioRecorderState) {
switch state {
case .error(let e): debugPrint(e)
case .Failed(let s): debugPrint(s)
case .Finish:
recodeBtn.setTitle("Recode", for: .normal)
case .Recording:
recodeBtn.setTitle("Recoding Finished", for: .normal)
case .Pause:
playBtn.setTitle("Pause", for: .normal)
case .Play:
playBtn.setTitle("Play", for: .normal)
case .Ready:
recodeBtn.setTitle("Recode", for: .normal)
playBtn.setTitle("Play", for: .normal)
refreshBtn.setTitle("Refresh", for: .normal)
}
debugPrint(state)
}
func agAudioRecorder(_ recorder: AGAudioRecorder, currentTime timeInterval: TimeInterval, formattedString: String) {
debugPrint(formattedString)
}
}
Swift 3 Code Version: Complete Solution for Audio Recording!
import UIKit
import AVFoundation
class ViewController: UIViewController, AVAudioRecorderDelegate {
//Outlets
#IBOutlet weak var recordingTimeLabel: UILabel!
//Variables
var audioRecorder: AVAudioRecorder!
var meterTimer:Timer!
var isAudioRecordingGranted: Bool!
override func viewDidLoad() {
super.viewDidLoad()
switch AVAudioSession.sharedInstance().recordPermission() {
case AVAudioSessionRecordPermission.granted:
isAudioRecordingGranted = true
break
case AVAudioSessionRecordPermission.denied:
isAudioRecordingGranted = false
break
case AVAudioSessionRecordPermission.undetermined:
AVAudioSession.sharedInstance().requestRecordPermission() { [unowned self] allowed in
DispatchQueue.main.async {
if allowed {
self.isAudioRecordingGranted = true
} else {
self.isAudioRecordingGranted = false
}
}
}
break
default:
break
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
audioRecorder = nil
}
//MARK:- Audio recorder buttons action.
#IBAction func audioRecorderAction(_ sender: UIButton) {
if isAudioRecordingGranted {
//Create the session.
let session = AVAudioSession.sharedInstance()
do {
//Configure the session for recording and playback.
try session.setCategory(AVAudioSessionCategoryPlayAndRecord, with: .defaultToSpeaker)
try session.setActive(true)
//Set up a high-quality recording session.
let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
//Create audio file name URL
let audioFilename = getDocumentsDirectory().appendingPathComponent("audioRecording.m4a")
//Create the audio recording, and assign ourselves as the delegate
audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
audioRecorder.delegate = self
audioRecorder.isMeteringEnabled = true
audioRecorder.record()
meterTimer = Timer.scheduledTimer(timeInterval: 0.1, target:self, selector:#selector(self.updateAudioMeter(timer:)), userInfo:nil, repeats:true)
}
catch let error {
print("Error for start audio recording: \(error.localizedDescription)")
}
}
}
#IBAction func stopAudioRecordingAction(_ sender: UIButton) {
finishAudioRecording(success: true)
}
func finishAudioRecording(success: Bool) {
audioRecorder.stop()
audioRecorder = nil
meterTimer.invalidate()
if success {
print("Recording finished successfully.")
} else {
print("Recording failed :(")
}
}
func updateAudioMeter(timer: Timer) {
if audioRecorder.isRecording {
let hr = Int((audioRecorder.currentTime / 60) / 60)
let min = Int(audioRecorder.currentTime / 60)
let sec = Int(audioRecorder.currentTime.truncatingRemainder(dividingBy: 60))
let totalTimeString = String(format: "%02d:%02d:%02d", hr, min, sec)
recordingTimeLabel.text = totalTimeString
audioRecorder.updateMeters()
}
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
//MARK:- Audio recoder delegate methods
func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
if !flag {
finishAudioRecording(success: false)
}
}
}
Simple 2022 syntax, record audio on iPhone.
Older answers don't work.
Needed ...
var mic: AVAudioRecorder?
var workingFile: URL {
return FileManager.default.temporaryDirectory.appendingPathComponent("temp.m4a")
}
And then
#IBAction public func tapTalkSend() {
switch AVCaptureDevice.authorizationStatus(for: .audio) {
case .authorized: _talkSend()
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
if granted { self?._talkSend() }
}
case .denied: return
case .restricted: return
#unknown default:
return
}
}
And then
public func _talkSend() {
if mic != nil && mic!.isRecording {
mic?.stop()
mic = nil
return
}
do {
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
mic = try AVAudioRecorder(url: workingFile, settings: [:])
}
catch let error {
return print("mic drama \(error)")
}
mic!.delegate = self
mic!.record()
}
Add AVAudioRecorderDelegate on your vc. And:
func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
_testPlay() ...
_sendFileSomewhere() ...
}
func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder, error: Error?) {
print("audioRecorderEncodeErrorDidOccur")
}
To test by playing it back:
var myPlayer: AVAudioPlayer!
func _testPlay() {
do {
myPlayer = try AVAudioPlayer(contentsOf: workingFile)
myPlayer.prepareToPlay()
myPlayer.play()
}
catch let error {
return print("play drama \(error)")
}
}
In your plist:
<key>NSCameraUsageDescription</key>
<string>For spoken messages.</string>
<key>NSMicrophoneUsageDescription</key>
<string>For spoken messages.</string>