Reputation: 159
I am integrating a documentPicker into my IOS app. Selected files will be uploaded using Firebase Storage. I am getting this error when attempting to upload the file while testing the app on my iPhone:
Error uploading file: The file “file.pdf” couldn’t be opened because you don’t have permission to view it.
On the other hand, am not getting this or any other error when testing using the simulator, and the error occurs whether I select a file from iCloud or on local storage.
Here is the code for picker:
struct DocumentPicker: UIViewControllerRepresentable {
@Binding var filePath: URL?
func makeCoordinator() -> DocumentPicker.Coordinator {
return DocumentPicker.Coordinator(parent1: self)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<DocumentPicker>) -> UIDocumentPickerViewController {
let picker = UIDocumentPickerViewController(documentTypes: ["public.item"], in: .open)
picker.allowsMultipleSelection = false
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: DocumentPicker.UIViewControllerType, context: UIViewControllerRepresentableContext<DocumentPicker>) {
}
class Coordinator: NSObject, UIDocumentPickerDelegate {
var parent: DocumentPicker
init(parent1: DocumentPicker){
parent = parent1
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
// Here is where I get the path for the file to be uploaded
parent.filePath = urls[0]
print(urls[0].absoluteString)
}
}
}
Here is the upload function, and where the error is caught:
do {
let fileName = (PickedDocument?.lastPathComponent)!
let fileData = try Data(contentsOf: PickedDocument!)
let StorageRef = Storage.storage().reference().child(uid + "/" + doc.documentID + "/" + fileName)
StorageRef.putData(fileData, metadata: nil) { (metadata, error) in
guard let metadata = metadata else {
return
}
StorageRef.downloadURL { (url, error) in
guard let urlStr = url else{
completion(nil)
return
}
let urlFinal = (urlStr.absoluteString)
ShortenUrl(from: urlFinal) { result in
if (result != "") {
print("Short URL:")
print(result)
completion(result)
}
else {
completion(nil)
return
}
}
}
}
}
catch {
print("Error uploading file: " + error.localizedDescription)
self.IsLoading = false
return
}
}
Obviously there is some sort of permission that I need to request in order to be able to access and upload files on a physical iPhone, but am not sure how to get that? I have tried adding the following in info.plist
but it still didn't work.
<key>NSDocumentsFolderUsageDescription</key>
<string>To access device files for upload upon user request</string>
Upvotes: 3
Views: 4067
Reputation: 159
I figured it out based on an answer I found here.
What I was doing is: I was storing a reference to the local file, which is a security scoped resource. Hence the permission error was thrown.
What I did to get around this: At the moment when the user picks the file, I will use url.startAccessingSecurityScopedResource()
to start accessing its content and make a copy of the file instead of holding its reference.
Here is the updated code for the picker didPickDocumentsAt
:
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard controller.documentPickerMode == .open, let url = urls.first, url.startAccessingSecurityScopedResource() else { return }
defer {
DispatchQueue.main.async {
url.stopAccessingSecurityScopedResource()
}
}
do {
let document = try Data(contentsOf: url.absoluteURL)
parent.file = document
parent.fileName = url.lastPathComponent
print("File Selected: " + url.path)
}
catch {
print("Error selecting file: " + error.localizedDescription)
}
}
And then my Storage upload function is:
func UploadFile(doc: DocumentReference, completion:@escaping((Bool?, String?) -> () )) {
do {
let StorageReference = Storage.storage().reference().child(self.User + "/" + doc.documentID + "/" + fileName!)
StorageReference.putData(file!, metadata: nil) { (metadata, error) in
if let error = error {
self.alertMessage = error.localizedDescription
self.showingAlert = true
completion(false, nil)
}
else {
StorageReference.downloadURL { (url, error) in
if let error = error {
self.alertMessage = error.localizedDescription
self.showingAlert = true
completion(false, nil)
}
else {
ShortenUrl(from: url!.absoluteString) { result in
if (result != "") {
completion(true, result)
}
else {
completion(false, nil)
}
}
}
}
}
}
}
catch {
self.alertMessage = "Error uploading file: " + error.localizedDescription
self.showingAlert = true
completion(false, nil)
}
}
Upvotes: 3