I would to play an m4a file but I'm getting an error in the console and the file won't play.
Console prints:
The operation couldn’t be completed. (OSStatus error 2003334207.)
let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
print("File Location: ",url.path)
if(FileManager.default.fileExists(atPath: url.path)) {
do {
audioPlayer = try AVAudioPlayer(contentsOf: url)
guard let player = audioPlayer else { return }
player.prepareToPlay()
} catch let error {
print(error.localizedDescription)
}
} else {
print("file not found")
}
I tried URL like this:
let NoteUrl = Bundle.main.url(forResource: "Voice Note", withExtension: "m4a")
But I couldn't get it to play. Can someone help me to play m4a file by file's name? Any help will be appreciated.
You are passing the Documents folder to the audio player. Your code needs to append the specific filename to the Documents folder.
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let url = documentsURL.appendingPathComponent("Voice Note.m4a")
Related
I am working on a app with recording function and followed a tutorial on this. I would like to edit the function where the AudioRecorder saves the file so that the file goes in the same folder it would if I dragged an audiofile directly in to xcode.
I am having trouble fetching a singe recording from the directory the Audiorecorder is saving to atm and found another tutorial that shows how to find recordings based on name from the app folder.
This is the recordings func today:
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]
print("AUDIORECORDER document path: \(documentPath)")
let audioFilename = documentPath.appendingPathComponent("\(Date().toString(dateFormat: "dd-MM-YY_'at'_HH:mm:ss")).m4a")
print("AUDIORECORDER audioFilename: \(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")
}
}
Any idea on how to do this? The tutorial only shows how to fetch all recordings to a list and I am trying to fetch a single recording. This is the fetch function if this helpes:
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)
}
objectWillChange.send(self)
}
It does not really matter where it saves but I need to be able to save the name to a string (So that I can fetch the right audio to the right object) and then find the audio based on that string.
Thank you.
I know there are a few questions pertaining to this, but they're in Objective-C.
How can I access a .txt file included in my app using Swift on an actual iPhone? I want to be able to read and write from it. Here are my project files if you want to take a look. I'm happy to add details if necessary.
Simply by searching in the app bundle for the resource
var filePath = NSBundle.mainBundle().URLForResource("file", withExtension: "txt")
However you can't write to it because it is in the app resources directory and you have to create it in the document directory to write to it
var documentsDirectory: NSURL?
var fileURL: NSURL?
documentsDirectory = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last!
fileURL = documentsDirectory!.URLByAppendingPathComponent("file.txt")
if (fileURL!.checkResourceIsReachableAndReturnError(nil)) {
print("file exist")
}else{
print("file doesnt exist")
NSData().writeToURL(fileURL!,atomically:true)
}
now you can access it from fileURL
EDIT - 28 August 2018
This is how to do it in Swift 4.2
var filePath = Bundle.main.url(forResource: "file", withExtension: "txt")
To create it in the document directory
if let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last {
let fileURL = documentsDirectory.appendingPathComponent("file.txt")
do {
if try fileURL.checkResourceIsReachable() {
print("file exist")
} else {
print("file doesnt exist")
do {
try Data().write(to: fileURL)
} catch {
print("an error happened while creating the file")
}
}
} catch {
print("an error happened while checking for the file")
}
}
Swift 3, based on Karim’s answer.
Reading
You can read files included in an app’s bundle through the bundle’s resource:
let fileURL = Bundle.main.url(forResource:"filename", withExtension: "txt")
Writing
However, you can’t write there. You will need to create a copy, preferably in the Documents directory:
func makeWritableCopy(named destFileName: String, ofResourceFile originalFileName: String) throws -> URL {
// Get Documents directory in app bundle
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last else {
fatalError("No document directory found in application bundle.")
}
// Get URL for dest file (in Documents directory)
let writableFileURL = documentsDirectory.appendingPathComponent(destFileName)
// If dest file doesn’t exist yet
if (try? writableFileURL.checkResourceIsReachable()) == nil {
// Get original (unwritable) file’s URL
guard let originalFileURL = Bundle.main.url(forResource: originalFileName, withExtension: nil) else {
fatalError("Cannot find original file “\(originalFileName)” in application bundle’s resources.")
}
// Get original file’s contents
let originalContents = try Data(contentsOf: originalFileURL)
// Write original file’s contents to dest file
try originalContents.write(to: writableFileURL, options: .atomic)
print("Made a writable copy of file “\(originalFileName)” in “\(documentsDirectory)\\\(destFileName)”.")
} else { // Dest file already exists
// Print dest file contents
let contents = try String(contentsOf: writableFileURL, encoding: String.Encoding.utf8)
print("File “\(destFileName)” already exists in “\(documentsDirectory)”.\nContents:\n\(contents)")
}
// Return dest file URL
return writableFileURL
}
Example usage:
let stuffFileURL = try makeWritableCopy(named: "Stuff.txt", ofResourceFile: "Stuff.txt")
try "New contents".write(to: stuffFileURL, atomically: true, encoding: String.Encoding.utf8)
Just a quick update for using this code with Swift 4:
Bundle.main.url(forResource:"YourFile", withExtension: "FileExtension")
And the following has been updated to account for writing the file out:
var myData: Data!
func checkFile() {
if let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last {
let fileURL = documentsDirectory.appendingPathComponent("YourFile.extension")
do {
let fileExists = try fileURL.checkResourceIsReachable()
if fileExists {
print("File exists")
} else {
print("File does not exist, create it")
writeFile(fileURL: fileURL)
}
} catch {
print(error.localizedDescription)
}
}
}
func writeFile(fileURL: URL) {
do {
try myData.write(to: fileURL)
} catch {
print(error.localizedDescription)
}
}
This particular example is not the most flexible, but with a little bit of work you can easily pass in your own file names, extensions and data values.
🎁 Property Wrapper - Fetch and convert to correct data type
This simple wrapper helps you to load any file from any bundle in a cleanest way:
#propertyWrapper struct BundleFile<DataType> {
let name: String
let type: String
let fileManager: FileManager = .default
let bundle: Bundle = .main
let decoder: (Data) -> DataType
var wrappedValue: DataType {
guard let path = bundle.path(forResource: name, ofType: type) else { fatalError("Resource not found: \(name).\(type)") }
guard let data = fileManager.contents(atPath: path) else { fatalError("Can not load file at: \(path)") }
return decoder(data)
}
}
Usage:
#BundleFile(name: "avatar", type: "jpg", decoder: { UIImage(data: $0)! } )
var avatar: UIImage
You can define any decoder to match your needs
Get File From Bundle in Swift 5.1
//For Video File
let stringPath = Bundle.main.path(forResource: "(Your video file name)", ofType: "mov")
let urlVideo = Bundle.main.url(forResource: "Your video file name", withExtension: "mov")
Bundles are read only. You can use NSBundle.mainBundle().pathForResource to access the file as read-only, but for read-write access you need to copy your document to Documents folder or tmp folder.
Bundles can be written. You can use Bundle.main.path to overwrite file by adding it into Copy Bundles Resource.
I have to use a file from another bundle. So, following code worked for me. Needful when you work with a frameworks.
let bundle = Bundle(for: ViewController.self)
let fileName = bundle.path(forResource: "fileName", ofType: "json")
I have got multiple audio files on server.
I want to download all audio files first, and then when all are downloaded, i need to play them one after another.
What will be the best approach to achieve this?
Thanks!
if let audioUrl = URL(string: "http://freetone.org/ring/stan/iPhone_5-Alarm.mp3") {
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// lets create your destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
print(destinationUrl)
// to check if it exists before downloading it
if FileManager.default.fileExists(atPath: destinationUrl.path) {
print("The file already exists at path")
// if the file doesn't exist
} else {
// you can use NSURLSession.sharedSession to download the data asynchronously
URLSession.shared.downloadTask(with: audioUrl, completionHandler: { (location, response, error) -> Void in
guard let location = location, error == nil else { return }
do {
// after downloading your file you need to move it to your destination url
try FileManager.default.moveItem(at: location, to: destinationUrl)
print("File moved to documents folder")
} catch let error as NSError {
print(error.localizedDescription)
}
}).resume()
}
}
I use following method to add file (download) to document directory:
static func downloadFileWithLink(linkString : String){
// Create destination URL
let documentsUrl:URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let destinationFileUrl = documentsUrl.appendingPathComponent("downloadedFile")
//Create URL to the source file you want to download
let fileURL = URL(string: linkString)
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:fileURL!)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
do {
try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
} catch (let writeError) {
print("Error creating a file \(destinationFileUrl) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription);
}
}
task.resume()
}
It simply download file to document folder in sandbox. I looked for method that delete files in documents folder, and tried following:
static func deleteFiledInDocDirectory(){
let fileManager = FileManager.default
let tempFolderPath = NSTemporaryDirectory()
do {
let filePaths = try fileManager.contentsOfDirectory(atPath: tempFolderPath)
for filePath in filePaths {
try fileManager.removeItem(atPath: tempFolderPath + filePath)
}
} catch {
print("Could not clear temp folder: \(error)")
}
}
However, when i inspect sandbox, downloaded file is still here. How to delete it?
In deleteFiledInDocDirectory() you're using NSTemporaryDirectory instead of the documents directory that you originally saved the file to.
Change tempFolderPath to be set using following:
guard let tempFolderPath = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
return // documents directory not found for some reason
}
The full method:
static func deleteFiledInDocDirectory(){
guard let tempFolderPath = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first?.absoluteString else {
return // documents directory not found for some reason
}
let fileManager = FileManager.default
do {
let filePaths = try fileManager.contentsOfDirectory(atPath: tempFolderPath)
for filePath in filePaths {
try fileManager.removeItem(atPath: tempFolderPath + filePath)
}
} catch {
print("Could not clear temp folder: \(error)")
}
}
I'm testing my app in simulator.
I'm downloading file and getting it's local way like this one:
file:///Users/administrator/Library/Developer/CoreSimulator/Devices/4CDF286B-543F-4137-B5E2-C312E19B992F/data/Containers/Data/Application/E5F13797-A6A8-48A1-B3C3-FBC3D7A03151/Documents/4d13e04980d3.mp3
Now I want to play this file with AVAudioPlayer but I'm always getting this error:
file:///
Error Domain=NSOSStatusErrorDomain Code=2003334207 "(null)"
Code for playing:
var alertSound = NSURL(fileURLWithPath: "file:///Users/administrator/Library/Developer/CoreSimulator/Devices/4CDF286B-543F-4137-B5E2-C312E19B992F/data/Containers/Data/Application/E5F13797-A6A8-48A1-B3C3-FBC3D7A03151/Documents/4d13e04980d3.mp3")
print(alertSound)
var error:NSError?
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: alertSound)
audioPlayer.prepareToPlay()
audioPlayer.play()
} catch {
print(error)
}
How should I make it playing?
Under iOS8, the path that you have saved won't be valid across launches. The id you see "E5F13797-A6A8-48A1-B3C3-FBC3D7A03151" will change with each launch.
The solution is to save the filename and not the complete path, and to recreate the URL or complete path, by getting the path to the Documents (or tmp) folder and appending the filename to it.
Look at NSFileManager. The method URLForDirectory:inDomain:appropriateForURL:create:error: is what you should use.
let fileName = ("FILE_NAME.mp3")
let documentsDirectory = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let soundFileURL = documentsDirectory.URLByAppendingPathComponent(fileName)
AudioCenter.player.playURL(soundFileURL)
// create the soundFileURL for the word
let toAppendString = "\(String)" //
let documentsDirectory = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
soundFileURL = documentsDirectory.URLByAppendingPathComponent(toAppendString)
do {
self.player = try AVAudioPlayer(contentsOfURL: soundFileURL)
player.prepareToPlay()
player.volume = 1.0
player.play()
} catch let error as NSError {
print(error)
}