Search code examples
swiftaudioswift3savetoday-extension

Accessing File saved in today extension from project


I am trying to access a file I saved from my today extension.In my today extenson I did this to save the file:

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("Stop", for: .normal)
    } catch {
        finishRecording(success: false)
    }
}

func getDocumentsDirectory() -> URL {
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let documentsDirectory = paths[0]
    return documentsDirectory
}

I then tried to get the data for my AVAudio Player in the main part of the project using this code:

let path = Bundle.main.path(forResource: "recording.m4a", ofType:nil)! let url = URL(fileURLWithPath: path)

However, it gave the error: unexpectedly found nil while unwrapping an Optional value. Thanks for the help.


Solution

  • Your extension saves the file to its document directory and your app code is looking for the file in the app bundle. The app bundle only contains the resources that are distributed with the app. You'll need to delve into the file system.

    However, there's another problem. The extension and containing app don't share a documents directory. They each have their own container for writing data local to themselves. If you want to share data between them, it's a little more work. In summary:

    1. Create an app group identifier for the app and the extension to share.
    2. Use FileManager.containerURL(forSecurityApplicationGroupIdentifier:) to get the file URL for the shared container directory.
    3. From the container URL, append the file name.
    4. In the extension, you'll set up the AVAudioRecorder as usual and start recording.
    5. In the main app, you'll want to use the NSFileCoordinator API to ensure that only one process is writing to the file at a time. Hopefully, the AVAudioRecorder uses the NSFileCoordinator API internally, although I didn't immediately find confirmation of this.

    For more details about shared containers, see this blog post.