0

I want to upload audio files to Firebase Storage and then fetch them to play. I try to store the path of the in a global variable:

var fileURL: URL?

Currently I have the following code the record the audio using AVFoundation:

func startRecording() {
            let recordingSession = AVAudioSession.sharedInstance()
        
        do {
            try recordingSession.setCategory(.playAndRecord, mode: .default)
            try recordingSession.setActive(true)
        }
        catch {
            print("Failed to set up recording session")
        }
        
        let documentPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        
        let audioFilename = documentPath.appendingPathComponent("\(Date().toString(dateFormat: "dd-MM-YY_'at'_HH:mm:ss")).m4a")
        
        self.fileURL = audioFilename
        
        let settings = [
                    AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
                    AVSampleRateKey: 12000,
                    AVNumberOfChannelsKey: 1,
                    AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
                ]
        
        do {
            audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
            audioRecorder.record()
            
            recording = true
            
        }
        catch {
            print("Could not start recording with error: \(error)")
        }
    }

After this I want to to stop the recording but also upload to file to Firebase. Currently, I have this piece of code which I have written referring to the Firebase documentation based on uploading files form a local reference point. I am not sure how to access to the data of the audio file or what is a good way to upload it directly to Firebase Storage. Here is the full ViewModel code:

import Foundation
import AVFoundation
import SwiftUI
import Combine
import Firebase

class VoiceViewModel : NSObject ,ObservableObject {
    
    override init() {
        super.init()
        fetchRecordings()
    }
    
    
    var fileURL: URL?
    
    let objectWillChange = PassthroughSubject<VoiceViewModel, Never>()
    
    var audioRecorder: AVAudioRecorder!
    
    var recordings = [Recording]()
    
    
    
    @Published var recording = false {
            didSet {
                objectWillChange.send(self)
            }
        }
    
    func startRecording() {
            let recordingSession = AVAudioSession.sharedInstance()
        
        do {
            try recordingSession.setCategory(.playAndRecord, mode: .default)
            try recordingSession.setActive(true)
        }
        catch {
            print("Failed to set up recording session")
        }
        
        let documentPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        
        let audioFilename = documentPath.appendingPathComponent("\(Date().toString(dateFormat: "dd-MM-YY_'at'_HH:mm:ss")).m4a")
        
        self.fileURL = audioFilename
        
        let settings = [
                    AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
                    AVSampleRateKey: 12000,
                    AVNumberOfChannelsKey: 1,
                    AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
                ]
        
        do {
            audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
            audioRecorder.record()
            
            recording = true
            
        }
        catch {
            print("Could not start recording with error: \(error)")
        }
    }
    
    func stopRecording() {
        audioRecorder.stop()
        recording = false
        
        let storageRef = Storage.storage().reference()
        
        let localfile = self.fileURL ?? URL(string: "")
        
        let fileRef = storageRef.child("images/try.m4a")
        
        print(self.fileURL?.absoluteURL)
        let uploadTask = fileRef.putFile(from: localfile!, metadata: nil) { metaData, err in
            guard let metadata = metaData else { return }
            
            if let err = err {
                print(err.localizedDescription)
                return
            }
            
            fileRef.downloadURL { (url, err) in
                guard let downloadUrl = url else { return }
                
                print(downloadUrl)
            }
            
        }
        
        
        fetchRecordings()
    }
       
    
    func fetchRecordings() {
            recordings.removeAll()
            
            let fileManager = FileManager.default
            let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
            let directoryContents = try! fileManager.contentsOfDirectory(at: documentDirectory, includingPropertiesForKeys: nil)
            for audio in directoryContents {
                let recording = Recording(fileURL: audio, createdAt: getCreationDate(for: audio))
                recordings.append(recording)
                
                recordings.sort(by: { $0.createdAt.compare($1.createdAt) == .orderedAscending})
                objectWillChange.send(self)
            }
        }    
}
cemgem
  • 17
  • 1
  • 6
  • You seem to have code written already for uploading the file. Can you clarify where the problem is or where you're getting result that you don't expect? Also, side note, you don't need the `didSet { objectWillChange.send(self) } ` on your `@Published` property -- that was only necessary in the beta period of SwiftUI 1.0. – jnpdx May 07 '21 at 22:45
  • When I call the startRecording function, I do not get any errors. When I call the stopRecording function I get the following error: AddInstanceForFactory: No factory registered for id. I am not sure where the problem is. When I print out the self.fileURL function, I get back the local path. – cemgem May 07 '21 at 22:53
  • Did you search SO for this? This question and answers seems to deal with this issue: https://stackoverflow.com/questions/58360765/swift-5-1-error-plugin-addinstanceforfactory-no-factory-registered-for-id-c – jnpdx May 07 '21 at 22:55
  • Yes, this is about the AVPlayer. I get the error when I try to upload the file to Firebase. – cemgem May 07 '21 at 22:57
  • The 'audioRecorder.stop()' function comes from the 'AVAudioRecorder' class. And the audio fille gets saved in the device without any problems. However, the file does not get uploaded to Firebase Storage. – cemgem May 07 '21 at 23:02
  • You're returning before printing the error in the event that `metaData` is `nil` -- are you sure there's no error? – jnpdx May 08 '21 at 00:03
  • Yeah, weirdly there is no error. So do you think the Firebase function is allowed to reach the audio file in the device? – cemgem May 08 '21 at 07:02
  • Yes -- if you can write it, you can read it. – jnpdx May 08 '21 at 07:08
  • I get these in my log during the process of recording an audio in the app: 'App Delegate does not conform to UIApplicationDelegate protocol.' ' AddInstanceForFactory: No factory registered for id – cemgem May 08 '21 at 07:49
  • Monitor the upload and see what happens: https://firebase.google.com/docs/storage/ios/upload-files#monitor_upload_progress – jnpdx May 08 '21 at 07:53
  • I did an in turned the .unauthorized case with description: // User doesn't have permission to access file – cemgem May 08 '21 at 08:15
  • However, before that it also printed out the 'percentComplete' too. Here is the exact log: 0.22237496462216472 0.22237496462216472 unauth – cemgem May 08 '21 at 08:16
  • It was about the Firebase Storage rules. Thanks mate – cemgem May 08 '21 at 08:18

0 Answers0