Swift AVAudioPlayer (Xcode) - Play Audio File Outside Application Bundle - swift

In my Xcode project, I have the following code set up (simplified code):
import Cocoa
import AVKit
class ViewController: NSViewController {
var audioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
do {
guard let filePath = Bundle.main.path(forResource: "sample", ofType: "mp3") else {
print("ERROR: Failed to retrieve music file Path")
return
}
let fileURL = URL.init(fileURLWithPath: filePath)
print(" PATH: \(filePath)")
print(" URL: \(fileURL)")
try audioPlayer = AVAudioPlayer.init(contentsOf: fileURL)
} catch {
print("ERROR: Failed to retrieve music file URL")
}
audioPlayer.play()
}
}
//CONSOLE PRINTS ----------:
// PATH: /Users/vakho/Library/Developer/Xcode/DerivedData/MusicPlayer-foqzsckozmcjnofvlvhuwabfssqi/Build/Products/Debug/MusicPlayer.app/Contents/Resources/sample.mp3
// URL: file:///Users/vakho/Library/Developer/Xcode/DerivedData/MusicPlayer-foqzsckozmcjnofvlvhuwabfssqi/Build/Products/Debug/MusicPlayer.app/Contents/Resources/sample.mp3
I am successfully able to pass the filePath of sample.mp3 (contained in application bundle) to audioPlayer by converting to URL first. Calling play() function plays the audio file.
However, since I am creating a music player app, I would like to be able to play an audio file that resides in directory folders, such as desktop, downloads folder, etc.... But when I attempt to pass a filePath of audio file outside app bundle, the code breaks:
import Cocoa
import AVKit
class ViewController: NSViewController {
var audioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
do {
let filePathOptional: String? = "/Users/vakho/Downloads/sample.mp3"
guard let filePath = filePathOptional else {
print("ERROR: Failed to retrieve music file Path")
return
}
let fileURL = URL.init(fileURLWithPath: filePath)
print(" PATH: \(filePath)")
print(" URL: \(fileURL)")
try audioPlayer = AVAudioPlayer.init(contentsOf: fileURL)
} catch {
print("ERROR: Failed to retrieve music file URL")
}
audioPlayer.play()
}
}
//CONSOLE PRINTS ----------:
// PATH: /Users/vakho/Downloads/sample.mp3
// URL: file:///Users/vakho/Downloads/sample.mp3
//ERROR: Failed to retrieve music file URL
//ERRORS ----------:
// (lldb)
// Thread 1: EXC_BAD_ACCESS (code=1, address=0x38)
From what I could conclude, AVAudioPlayer and AVKit library is designed for accessing app assets and bundle media files, as well as streaming media from the internet. But it fails to play media from directory.
I have found several threads about the issue, all incomplete or unanswered, such as: https://forums.developer.apple.com/thread/18272
So if AVKit cannot do what I thought it should have, what library or approach can I use to access directory files? Music player apps for OSX are of course able to prompt user to scan directory and access music files. How are they doing it?

Seems that problem is here:
guard let filePath = filePathOptional else {
print("ERROR: Failed to retrieve music file Path")
return
}
Please change the print to:
print("ERROR: Failed to retrieve music file Path \(error.localizedDescription)")
I think you'll see that the problem is that you have no access to this file.
By default you have access only to sandbox of your app and Desktop folder.
Also try to put your file to Desktop and play it.

Related

How to make sure if the video recorded is related to a specific App and not the device's Camera before I delete it Swift

I'm using AVCaptureOutPut to record a video and upload to the server. I'm deleting the last video taken once I upload to the server. Everything works fine.
Now I want to delete the video if the user recorded but doesn't want to upload and close the App. I've added same function to the AppDeletage's applicationWillTerminate function but it's picking up the personal video from iCloud if the user goes to the Record screen but like to close the App without recording.
How do I check if the particular video is recorded using the App and not thru the Camera?
Here is my code:
Recording and Saving Video in CaptureView Controller:
#IBAction func handleMoviewRecord(_ sender: Any) {
if videoRecordState == .stop {
// let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
// let fileUrl = paths[0].appendingPathComponent("output.mov")
// try? FileManager.default.removeItem(at: fileUrl)
let tmpdir = NSTemporaryDirectory()
outputPath = "\(tmpdir)output.mov"
outputURL = NSURL(fileURLWithPath:outputPath as String)
if FileManager.default.fileExists(atPath: outputPath) {
do {
try FileManager.default.removeItem(atPath: outputPath)
} catch _ {
}
}
}
}
}
unc fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
print("FINISHED \(Error.self)")
// save video to camera roll
if error == nil {
if FileManager.default.fileExists(atPath: outputPath){
if UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(self.outputPath) {
UISaveVideoAtPathToSavedPhotosAlbum(outputPath, nil, nil, nil)
// outputFileURL.path, nil, nil, nil)
UserDefaults.set(.videoPath, to: outputURL.path)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "refresh"), object: nil)
navigationController?.popViewController(animated: true)
}
}
}
}
App Delegate.Swift
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
self.saveContext()
clearTmpDirectory()
}
func clearTmpDirectory() {
do {
let tmpDirURL = FileManager.default.temporaryDirectory
let filePaths = try FileManager.default.contentsOfDirectory(atPath: "\(tmpDirURL)")
for filePath in filePaths {
try FileManager.default.removeItem(atPath: NSTemporaryDirectory() + filePath)
}
} catch {
//catch the error somehow
}
}
You should not save it to the user's photo library. Just use the temporary file folder and stage your data there before uploading to your server.
let directory = FileManager.default.temporaryDirectory
/// create a new file there and write capture data to this directory

Xcode swift - AVAudio Player not working after Copy Bundle

I am working on using AVAudio player and I am currently getting the following error - Fatal error: Unexpectedly found nil while unwrapping an Optional value: file
I looked and on various solutions the problem was that the desired file was not in the copy bundle resources area. But the file has been added there to the desired target, so not sure of the solution.
import Foundation
import Capacitor
#objc(Buckfast)
public class Buckfast: CAPPlugin {
#objc func echo(_ call: CAPPluginCall) {
let value = call.getString("value") ?? ""
call.success([
"value": value
])
var bombSoundEffect: AVAudioPlayer?
if let path = Bundle.main.path(forResource: "1", ofType: "wav") {
let url = URL(fileURLWithPath: path)
do {
bombSoundEffect = try AVAudioPlayer(contentsOf: url)
bombSoundEffect?.play()
} catch {
// couldn't load file :(
}
}
}
}
Copy Bundle Image
Code Screenshot
You can try to unwrap using if let like
if let path = Bundle.main.path(forResource: "1", ofType: "wav") {
}
The file was not found when residing in the pods folder. The file needs to be placed in the Apps Copy Bundle Resources instead.

Can not play sound file Swift4

I'm trying play simple .wav file in my application.
Here is the code:
func alarmSound() {
var player: AVAudioPlayer?
guard let path = Bundle.main.path(forResource: "emergency021", ofType: "wav") else {
print("Can't find audio file")
return
}
let url = URL(fileURLWithPath: path)
do {
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.wav.rawValue)
if let prepare = player?.prepareToPlay() {
print("Player prepared: \(prepare)")
}
player?.volume = 1.0
player?.play()
} catch {
print("Playing sound error: \(error)")
}
}
Everything ok, file in the bundle, url is ok, player prepared, but.. there is no any sound. I have tried .mp3 file - same result.
Any ideas, why?
Issue was declaring player: AVAudioPlayer locally in function body, when I moved it up to ViewController all works.

Read file with swift in iphone

I need to read a file with swift in my iphone.
In my computer I use this code and function correctly. The file "test.txt" is in my Desktop.
import UIKit
class Controller: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let sourcePath = URL(fileURLWithPath: "/Users/myname/Desktop", isDirectory: true)
let file : URL = URL(fileURLWithPath: "test.txt", relativeTo: sourcePath)
let filemgr = FileManager.default
if filemgr.fileExists(atPath: file.path){
do{
//Code to Parse text
} catch let error as NSError{
print ("Error: \(error)")
}
}
}
}
The problem is that I need to read this file in my iphone. But I don't know which is the URL to read the file. Where I can save the file and which is the URL?
Thanks
Simple answer is for this question is you can not read file from you desktop to your device. That file should be in your app document directory or you need to add that file into your project.
After that you can read it into device.
1- Drag the text file to the project and select copy Items If needed
2- Use this to read it
do {
if let path = Bundle.main.path(forResource: "fileName", ofType: "txt") {
let str = try String(contentsOfFile:path, encoding: .utf8)
print(str)
}
}
catch {
print(error)
}

fatal error: unexpectedly found nil while unwrapping an Optional value when using AudioPlayer in Swift 2

Hi I am trying to play a music file with a following code in swift 2. Basically I just dragged the audio file with a name f.mp3 to the asses folder and it my code breaks with the following message:
unexpectedly found nil while unwrapping an Optional value. Where exactly I need to put my mp3 file so the IOS can find it. Thank you
var audioPlayer: AVAudioPlayer! = nil
func playMyFile() {
let path = NSBundle.mainBundle().pathForResource("f", ofType: "mp3")
let fileURL = NSURL(fileURLWithPath: path)
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: fileURL)
} catch {
print("error")
}
audioPlayer.prepareToPlay()
audioPlayer.delegate = self
audioPlayer.play()
}
Your code is working fine with my project and here is my complete code:
import UIKit
import AVFoundation
class ViewController: UIViewController {
var audioPlayer: AVAudioPlayer! = nil
override func viewDidLoad() {
super.viewDidLoad()
playMyFile()
}
func playMyFile() {
let path = NSBundle.mainBundle().pathForResource("f", ofType: "mp3")
let fileURL = NSURL(fileURLWithPath: path!)
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: fileURL)
} catch {
print("error")
}
audioPlayer.prepareToPlay()
audioPlayer.play()
}
}
Make sure your audio is added into Copy Bundle Resources like this:
If not added then add it this way:
Check THIS sample for more Info.