AVAudioRecorder/AVAudioPlayer not able to open previously saved file - ios

I can successfully record audio using AVAudioRecorder. I can successfully play this recording using AVAudioPlayer.
This is saved to the documents folder:
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
However, when i try to re-open this file (to either record over it or play it back) i am unable to do so. I get recording state or player state to return false.
An example of the file name/URL:
file:///var/mobile/Containers/Data/Application/30025787-C4F5-4F23-A345-C38EE44A180B/Documents/E678B939-730D-40D8-84EC-DE79FE4BDE3D.m4a
Note that i try to re-open the file like this:
self.audioRecorder = try AVAudioRecorder(url: self.recordedFileURL!, settings: settings)
self.audioRecorder.delegate = self
self.audioRecorder.isMeteringEnabled = true
let prepared = self.audioRecorder.prepareToRecord()
print("prepared to record = \(prepared)") // this returns FALSE when i am trying to re-open. But returns TRUE when i record for the first time.
When i try to open the file with the AVAudioPlayer i get error:
The operation couldn’t be completed. (OSStatus error 2003334207.)
The code to open the file using the saved URL is this:
if (self.recordedFileURL != nil) {
do {
self.audioPlayer = try AVAudioPlayer(contentsOf: self.recordedFileURL!)
self.audioPlayer.delegate = self
self.audioPlayer.volume = 1.0
self.audioPlayer.prepareToPlay()
}
catch let error {
print("Play audio error: " + error.localizedDescription)
}
}
* Updated *
I am beginning to think the file is actually not saved in the documents folder. I see this error in the output window:
fatal error: unexpectedly found nil while unwrapping an Optional value
2016-11-07 09:38:28.177535 fatal error: unexpectedly found nil while unwrapping an Optional value
How to verify this file exists in the documents folder?
* Updated *
I suspect I am saving the file to a temporary location. I will need to investigate in this direction once I get home. Here is a reference.
* updated 2 *
I found a better more precise reference here.
* RESOLVED *
I believe there were a couple (2) issues.
1) I made sure that I'm not using the path name of a file that already exists. If I use an existing path (which is the path to a recorded audio) to initialize the Recorder, it will override whatever audio was recorded. I had to make sure to use a separate/unique path for any new recording.
2) I had to ensure that I am working with the right URL paths. I found out that I wasn't consistent with this. In short, use the absolute string path, and use that consistently everywhere (reading & reading).

Related

NSFileManager moveItem throws Error Code=513

I'm trying to move a file from a URL to another, however I get an error with code 513. I understand that this is a NSFileWriteNoPermissionError. I don't see how this possible considering I created the folder in the first place.
//self.video!.folderURL.path = /var/mobile/Containers/Data/Application/066BDB03-FD47-48D8-B6F8-932AFB174DF7/Documents/AV/769c504203024bae95b47d78d8fe9029
try? fileManager.createDirectory(atPath: self.video!.folderURL.path, withIntermediateDirectories: true, attributes: nil)
// Update permission for AV folder
do {
let parentDirectoryPath = self.video!.folderURL.deletingLastPathComponent().path
let result = try fileManager.setAttributes([FileAttributeKey.posixPermissions: 0o777], ofItemAtPath: parentDirectoryPath)
print(result)
} catch {
print("Error = \(error)")
}
// sourceURL = file:///private/var/mobile/Containers/Data/PluginKitPlugin/50D8B8DB-19D3-4DD6-93DD-55F37CF87EA7/tmp/trim.6D37DFD2-5F27-4AB9-B478-5FED8AA6ABD7.MOV
// self.url = file:///var/mobile/Containers/Data/Application/066BDB03-FD47-48D8-B6F8-932AFB174DF7/Documents/AV/0b0b6803e891780850152eeab450a2ae.mov
do {
try fileManager.moveItem(at: sourceURL, to: self.url)
} catch {
QLogTools.logError("Error moving video clip file \(error)")
}
Error
Error Domain=NSCocoaErrorDomain Code=513 "“trim.90A10B33-B884-419F-BAD9-65583531C3C3.MOV” couldn’t be moved because you don’t have permission to access “AV”." UserInfo={NSSourceFilePathErrorKey=/private/var/mobile/Containers/Data/PluginKitPlugin/0849234B-837C-43ED-BEDD-DE4F79E7CE96/tmp/trim.90A10B33-B884-419F-BAD9-65583531C3C3.MOV, NSUserStringVariant=(
Move
I tried altering the permission of the folder I created in the first place to 0o777, but when I print out its attributes it returns 511 as its permissions.
P.S:This only happens on iOS 13.
I solved my issue, but it may be different to what you were experiencing: I still had issues when changing to copyItem. I'm adding my findings here as an answer in case anyone else stumbles upon this question as I did and it helps them out.
My app loads custom files that can be downloaded from my website. With iOS 13's download manager, these are copied to the Downloads folder first, and can then be opened by my app. Yet my app was having permission errors when attempting to access these files in the same way as iOS 12.
This answer to a different question clued me in. I need to call startAccessingSecurityScopedResource() and stopAccessingSecurityScopedResource() as follows:
// source and destination are URLs
if source.startAccessingSecurityScopedResource() {
try FileManager.default.moveItem(at: source, to: destination)
}
source.stopAccessingSecurityScopedResource()
The Apple documentation doesn't really suggest that this is new with iOS 13 but I think just the different approach to file downloading means sandboxing considerations need to be made.

How to add url in avaudioplayer in swift

i just want to ask on how to play audio using avAudion using url link..
and im getting error bad request.. can someone help me?
i already tried using avplayer but it is not suitable for me.. though it is working but.. i kinda prefer to use avaudioplayer
let mp3URL = NSURL(fileURLWithPath:"https://s3.amazonaws.com/kargopolov/kukushka.mp3")
do {
// 2
audioPlayer = try AVAudioPlayer(contentsOf: mp3URL as URL)
audioPlayer.play()
// 3
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateAudioProgressView), userInfo: nil, repeats: true)
progressView.setProgress(Float(audioPlayer.currentTime/audioPlayer.duration), animated: false)
}
catch {
print("An error occurred while trying to extract audio file")
}
According to the AVAudioPlayer documentation, if you want to play audio over the internet (that's not already downloaded to disk or memory) you should use AVPlayer.
The docs describe AVAudioPlayer as
An audio player that provides playback of audio data from a file or memory.
A little further down, in the overview section, emphasis added:
Use this class for audio playback unless you are playing audio captured from a network stream or require very low I/O latency.
Notes
You can get some more information from the runtime by accessing the error object that is thrown when the connection fails. Change catch { to catch let error { and then you can log out the error as part of your message, like so:
catch let error{
print("An error occurred while trying to extract audio file: \(error)")
}
When I run your sample code in Xcode playgrounds with the change noted above, I see the following:
An error occurred while trying to extract audio file: Error Domain=NSOSStatusErrorDomain Code=2003334207 "(null)"
Notice the error's Domain and Code. Pasting that error into Google yields some results that indicate that the URL might not be resolving correctly. (Indeed, clicking on that mp3 link shows a bad access error message.)
The first result is another StackOverflow post with a similar issues. The second one is an Apple Developer Forum post which has some more information.
Let's try to change the URL to a publicly accessible sample mp3 file. (I found this one by searching the web for "test mp3 file" on Google.)
You'll want to change NSURL to URL, and instead of fileURLWithPath, you're going to want to use another initializer. (Say, string:.)
Whenever you see contentsOf...: in a media or file API, there's a good chance it expects data or a file, to the exclusion of a network stream. Similarly, when you see an initializer or method that takes a fileURL..., the system expects to be pointing to a local resource, not a network URL.
if let mp3URL = URL(string: "https://s3.amazonaws.com/kargopolov/kukushka.mp3"){
// do s.t
}
Anyway, you should know the basics of ios

iOS 9 read file permission

In iOS 9+ I get a nil on any attempt to read from file. The file in this case is a image file path.
using
NSData(contentsOfFile: stringpath, options: NSDataReadingOptions.DataReadingUncached)
or
NSData(contentsOfFile: stringpath)
Actions:
I have removed the "file://" from the path and it now has a permissions issue.
Error Domain=NSCocoaErrorDomain Code=257 "The file “IMG_0048.JPG” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/var/mobile/Media/DCIM/100APPLE/IMG_0048.JPG, NSUnderlyingError=0x13f978f50 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
I have added the NSAllowArbitraryLoads and set it to true.
I have tried to look for the file myself using "NSSearchPathDirectory" however the paths do not match in any way
I encountered this error because I was attempting to access multiple files in the same block. The fix that worked for me was changing the code structure such that each file url was obtained, then read from, before attempting to get the next file url.
You are most likely getting this error, because iOS apps only have access to files within its sandbox. See Apple documentation on file systems for details.
In your Application, you don't have permission to access the file of /var/mobile/Media/DCIM/100APPLE/IMG_0048.JPG, because of Sandboxing.
So, whatever you do, you can't initialize NSData or UIImage with the file path. But you can access the file of /var/mobile/Media/DCIM/100APPLE/xxx.mov with AVURLAsset. In my application, I extracted the data rather than URL from gallery by Photos kit and initialized UIImage with the data.
PHImageManager.default().requestImageData(
for: assetObject!, options: options,
resultHandler: {
data, _, _, _ in
if data != nil {
self.assetUrl = movieMaker.createMovieFrom(imageData: data!, duration: Int(CXPreparetValue.imageDuration))
}
})
it works for me! If you have other opinions, please tell me.
In my case, the file permissions were too restrictive, so I couldn't read the file.
Adding read+write permissions to the file before accessing it solved it.
do {
// Retrieve any existing attributes
var attrs = try FileManager.default.attributesOfItem(atPath: stringpath)
let existing = (attrs as NSDictionary).filePosixPermissions()
// Set the read+write value in the attributes dict
attrs[.posixPermissions] = existing | 0b110000000
// Update attributes
try FileManager.default.setAttributes(attrs, ofItemAtPath: stringpath)
// Read data from file
let data = try Data(contentsOf: URL(fileURLWithPath: stringpath, isDirectory: false), options: .uncached)
print("success: \(data.count)")
} catch {
print(error)
}
That works if you're in a folder with enough permissions, as you can change the files permissions even if you didn't had read permission on the file previously. This solution was applied at https://github.com/ZipArchive/ZipArchive/issues/293.

realm.io writeCopyToPath ? what's the inverse operation?

so I have some realm swift code:
let realm = Realm()
realm.writeCopyToPath(tempfile! , encryptionKey: nil)
let newasset = CKAsset(fileURL: NSURL(fileURLWithPath: tempfile!, isDirectory: false))
and then I save off the newasset to cloudkit...
but when I bring it back,
let defaultPath = Realm().path
var someError: NSError?
NSFileManager.defaultManager().removeItemAtPath(defaultPath, error: &someError)
if someError != nil {
println("Error removing realm file, \(someError)")
}
NSFileManager.defaultManager().copyItemAtPath(asset.fileURL.path!, toPath: defaultPath, error: &someError)
if someError != nil {
println("Error copying realm file into place, \(someError)")
}
it does not seem to work... I'm wondering if there is another way to bring back the realm file and place it where realm expects it?
The default location and file name of the default Realm file is default.realm, located in the app's Documents directory. As long you've correctly copied the Realm file out of CloudKit and to that location with the proper file name, accessing that file via calling Realm() should just work straight out of the box.
It sounds like you've got a few points in here that you can potentially test to see if anything is failing:
1) Make sure the duplicate Realm file is being created properly.
2) Ensure the file is uploaded to CloudKit properly, without fail.
3) Ensure the file came back down from CloudKit properly without any errors.
4) Double-check that copying the file from CloudKit to the default Realm file path succeeded.
If that all didn't work, please let me know here what sort of error you're specifically getting. Thanks, and good luck!

iPad saving audio save to local MusicDirectory

iOS 8.2
iPad a1474
inside app on IPAD, my code is
var dirPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
user records voice and it saves to
Optional(file:///var/mobile/Containers/Data/Application/2B2028C4-8E7D-4D7F-9FAF-EB843F506A5E/Documents/audio-2015-01-25-144545.m4a)
in Itunes/App,
have tried
MusicDirectory and SharedPublicDirectory
with either of
LocalDomainMask or DocumentDirectory
none work with error
The operation couldn’t be completed. (OSStatus error 2003334207.)
fatal error: unexpectedly found nil while unwrapping an Optional value
so what gives?
why can't app save to local MusicDirectory on the iPad??? there is nothing in the docs to indicate that this is protected area on the iPad.
no issues with iTunes or OSX, this is an iOS issue.
Under iOS8, the path that you have saved won't be the same across launches. The id you see "410AB24E-5FB0-4401-AC59-3C03D676E951" will change with each launch.
The workaround 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.

Resources