5
votes

Any reason why Xcode would complain to me about permissions in regards to uploading an image to Firebase Storage but when I run the same app in the Simulator, it works fine?

Permissions Error:

Body file is unreachable: /var/mobile/Media/DCIM/100APPLE/IMG_0974.JPG Error Domain=NSCocoaErrorDomain Code=257 "The file “IMG_0974.JPG” couldn’t be opened because you don’t have permission to view it." UserInfo={NSURL=file:///var/mobile/Media/DCIM/100APPLE/IMG_0974.JPG, NSFilePath=/var/mobile/Media/DCIM/100APPLE/IMG_0974.JPG, NSUnderlyingError=0x14805d680 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}

6
not a firebase issue. iOS issue.rigdonmr
Thanks? I know that since it's working in the sim.Michael Williams
@EricD why would I not have permission to write to my own device though? I get that it's not a Firebase issue.Michael Williams
App sandboxing on the simulator is potentially different from a physical device, and it's likely that the /var directory isn't user writable on a physical device. See developer.apple.com/library/mac/documentation/FileManagement/… for full details (TL;DR: use /Documents and /tmp).Mike McDonald
have you find a solution ? same problem for meXero

6 Answers

7
votes

I'm having the same problem right now. Uploading works fine in the Simulator, but on a device it gives me the permission error. My workaround has been to upload the image as data rather than with the URL to the file:

let imageData = UIImageJPEGRepresentation(image, 1.0)
let uploadTask = storageReference.child(remoteFilePath).putData(imageData, metadata: metadata, completion: { (metadata, error) in
    // Your code here
}
1
votes

Uploading works fine in the Simulator, but on a device it gives me the permission error. Upload the image as data rather than with the URL to the file:

Declare first

    fileprivate lazy var storageRef: StorageReference = Storage.storage().reference(forURL: "gs://yourAPP.appspot.com/")

then

     let path : String = "\(String(describing: Auth.auth().currentUser?.uid))/\(Int(Date.timeIntervalSinceReferenceDate * 1000))/\(photoReference)"

                // 6

                let imageData = UIImageJPEGRepresentation(photoReference, 0.1)
                let uploadTask = self.storageRef.child(path).putData(imageData!, metadata: nil, completion: { (metadata, error) in
                    // Your code here
                    if let error = error {
                        print("Error uploading photo: \(error.localizedDescription)")
                        return
                    }

                    // 7
                    self.setImageURL(self.storageRef.child((metadata?.path)!).description, forPhotoMessageWithKey: key)
                })
0
votes

Another option is to copy the file to the "documents" directory (which is writable). It uses a bit more code than the UIImageJPEGRepresentation approach though so you may still prefer to use that technique instead. Perhaps use the "documents directory" approach if you really don't want to alter the original image (however, in theory, the difference between the original and one created using UIImageJPEGRepresentation at 100% should be imperceptible).

asset.requestContentEditingInput(with: options, completionHandler: {(contentEditingInput: PHContentEditingInput?, info: [AnyHashable : Any]) -> Void in

    let originalFileUrl = contentEditingInput!.fullSizeImageURL!

    /* Copy file from photos app to documents directory */
    let fileManager = FileManager.default
    let documentsDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
    let destinationPath = documentsDirectory.appendingPathComponent(originalFileUrl.lastPathComponent)
    let destinationUrl = URL(fileURLWithPath: destinationPath)

    do {
        try fileManager.copyItem(at: originalFileUrl, to: destinationUrl)
    }
    catch {
        print("Unable to copy file from photos to documents directory (run out of space on device?)")
        return
    }

    /* Create metadata etc. for Firebase... */

    /* Upload file to Firebase */
    fileRef.putFile(from: destinationUrl, metadata: metadata, completion: { (metadata, error) in

        // Remove the file from the documents directory
        do {
            try fileManager.removeItem(at: destinationUrl)
        }
        catch {
            print("Unable to remove file from documents directory")
        }

    })


})
0
votes

This code below in the didFinishPickingMediaWithInfo.

I commented the code that handle when the user picked image from the photoLibrary:

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
    picker.dismissViewControllerAnimated(true,completion:nil)
    let image = info[UIImagePickerControllerOriginalImage] as! UIImage//<--

    let imageData = UIImageJPEGRepresentation(image, 1.0)
    let imagePath = "photos/xxxyyyzzz.jpg"//whatever you want
    let metadata = FIRStorageMetadata()
    metadata.contentType = "image/jpeg"
            
    storageRef.child(imagePath).putData(imageData!,metadata:metadata){
                (metadata, error) in
                if let error = error{
                    print("Error uploading photo: \(error)")
                }
                else{
                    //if there is no error the compiler will come here
                    print("no error")
                    //and maybe you want to use the full url or download url after success
                    //for example you wanna have tha downloadURL...
                    let downloadURL = metadata!.downloadURL()
                    print("\(downloadURL)")
                    //do whatever you want...
                }
    }

}
0
votes

I'm using SwiftUI and .fileImporter it's still all very new to me, so I'm not sure if it's all right, but it's working now so I thought I'd leave the code here for someone struggling too. File Importer code

function to move the document before uploading

Add the FileManager extension

 extension FileManager {


   static func getDocumentsDirectory() -> URL {
       let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
       let documentsDirectory = paths[0]
       return documentsDirectory
   }
}
-1
votes

Check out the Firebase Storage Rules. and temporary allow all path without auth.