Reputation: 1185
I'm currently trying to import (audio) files from the system Files app to the app documents directory using the new fileImporter
.
Selecting files works well but apparently, my saveFile()
function doesn't save the file to the documents directory since listing all files with listAllFiles()
always returns an empty array. If I try to play a sound with the local (documents) URL it just doesn't work at all.
Update: Seems to be a permission issue. Getting the error message The file “XY.mp3” couldn’t be opened because you don’t have permission to view it.
using try FileManager.default.copyItem(at: url, to: actualPath)
.
Update 2: Tried to do it with UIKit. Same issue as well.
Does somebody have an idea what I am doing wrong? Thanks a lot for your help!
import SwiftUI
struct ContentView: View {
@State var importFiles = false
@State var files = [URL]()
var body: some View {
VStack{
Spacer()
Button(action: {
importFiles = true
}, label: {Text("Import files")})
.padding()
Button(action: {
listAllFiles()
}, label: {Text("List files")})
.padding()
Spacer()
ForEach(files, id: \.self){ file in
Text(file.lastPathComponent)
.padding()
}
}
.fileImporter(
isPresented: $importFiles, allowedContentTypes: [.audio],
allowsMultipleSelection: false
) { result in
do {
let selectedFiles = try result.get()
if(selectedFiles.count == 1){
files.append(saveFile(url: selectedFiles[0]))
}
else{
print("error")
}
} catch {
// Handle failure.
print("failed")
}
}
}
}
func getDocumentsDirectory() -> URL {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
}
func listAllFiles() {
let documentsUrl = getDocumentsDirectory()
do {
let directoryContents = try FileManager.default.contentsOfDirectory(at: documentsUrl, includingPropertiesForKeys: nil)
print(directoryContents)
} catch {
print(error)
}
}
func saveFile (url: URL) -> URL {
let fileData = try? Data.init(contentsOf: url)
let fileName = url.lastPathComponent
let actualPath = getDocumentsDirectory().appendingPathComponent(fileName)
do {
try fileData?.write(to: actualPath, options: .atomic)
print("success. origin: \(url) target: \(actualPath)")
} catch {
print("File could not be saved")
}
return actualPath
}
Upvotes: 2
Views: 1982
Reputation: 1185
After doing a lot of research, I found out that including CFURLStartAccessingSecurityScopedResource
and CFURLStopAccessingSecurityScopedResource
to the saveFile
function fixes the issue. However, I have absolutely no idea why. If anybody knows more, I'd be pleased to hear about it.
func saveFile (url: URL) {
if (CFURLStartAccessingSecurityScopedResource(url as CFURL)) { // <- here
let fileData = try? Data.init(contentsOf: url)
let fileName = url.lastPathComponent
let actualPath = getDocumentsDirectory().appendingPathComponent(fileName)
do {
try fileData?.write(to: actualPath)
if(fileData == nil){
print("Permission error!")
}
else {
print("Success.")
}
} catch {
print(error.localizedDescription)
}
CFURLStopAccessingSecurityScopedResource(url as CFURL) // <- and here
}
else {
print("Permission error!")
}
}
Upvotes: 4