ios app: Uploading video to Azure storage - error Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"

Hi,

I'm working in a iOS app using the UIImagePickerController to get files and videos from the photo library and upload to Azure Blob storage.

For images, everything is working fine, but for videos, it fails with the following error:

Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted" UserInfo={_kCFStreamErrorCodeKey=1, _kCFStreamErrorDomainKey=1}

Basically, what I'm doing is:

1.to get the file URL from the UIImagePickerController:

                if mediaType == "public.movie" {                     let mediaURL = info[UIImagePickerController.InfoKey.mediaURL] as! NSURL                                          uploadFileAsync(fileURL: mediaURL.filePathURL)

                }

  1. the upload to Azure method:

 func uploadFileAsync(fileURL: URL) { ...

do { let isSecureAccess = fileURL.startAccessingSecurityScopedResource()

                defer{fileURL.stopAccessingSecurityScopedResource()}
                let resourceValues = try fileURL.resourceValues(forKeys: [.fileSizeKey])
                // Gets the Mime Type
                let extUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileURL.pathExtension as CFString, nil)?.takeUnretainedValue()
                let mimeType:String = extUTI != nil ? (UTTypeCopyPreferredTagWithClass(extUTI!, kUTTagClassMIMEType)?.takeUnretainedValue() as String?) ?? defaultMimeType : defaultMimeType
                let  fileSize: Int = resourceValues.fileSize!
                blockBlob?.properties.length = NSNumber(value: fileSize)
                blockBlob?.properties.contentType  = mimeType
                blockBlob?.uploadFromFile(with: fileURL)

}

The error is generating from the blockBlob?.uploadFromFile(with: fileURL)

Any help is really appreciated.

Answered by DTS Engineer in 705709022

Unfortunately I can’t help you with a third-party library. If you have questions about that, you need to investigate the code yourself, assuming its open source, or seek support from the library vendor.

If this is library is using a standard session, you may be able to prevent this error by deferring your call to stopAccessingSecurityScopedResource() until after the upload is complete.

When you think this it makes sense: The URLSession code running in your process doesn’t know that the URL is security scoped and thus doesn’t call startAccessingSecurityScopedResource() / stopAccessingSecurityScopedResource() around its access. Rather, it assumes that it has access to the URL, which means that you have to make these calls on its behalf.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Are you able to copy the file into a temporary directory during the callback?

Yes, if I try to save the file into a temporary directory, it works fine

Cool.

but we don't want to duplicate store, and also, we don't want to impact performance for large files.

Keep in mind that iOS uses APFS and APFS’s copy-on-write feature means that file copies are very cheap (both in terms of performance and disk space).

Still, I understand your sentiment so…

Are you using a URLSession background session? Or a standard session?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer

Unfortunately I can’t help you with a third-party library. If you have questions about that, you need to investigate the code yourself, assuming its open source, or seek support from the library vendor.

If this is library is using a standard session, you may be able to prevent this error by deferring your call to stopAccessingSecurityScopedResource() until after the upload is complete.

When you think this it makes sense: The URLSession code running in your process doesn’t know that the URL is security scoped and thus doesn’t call startAccessingSecurityScopedResource() / stopAccessingSecurityScopedResource() around its access. Rather, it assumes that it has access to the URL, which means that you have to make these calls on its behalf.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi @eskimo

Hi, I don't think we are missing the startAccessingSecurityScopedResource call and defering the stopAccessingSecurityScopedResource call , as you can see in our code. The interesting thing is why this works fine for images, but not for videos, so there should be something else that can help to avoid the issue we are experiencing. Something interesting I found is the fileURL returned by the ImagePicker for both types use different base (video uses PluginKitPlugin):

Image: "file:///private/var/mobile/Containers/Data/Application/889C8088-35CA-49A1-9F6E-DADA623C1376/tmp/64970B3E-F3B6-4B22-AEFE-57AF9077F2B8.jpeg"

 Video "file:///private/var/mobile/Containers/Data/PluginKitPlugin/50BF42DE-ED15-476C-AE5C-B118D1A4FA8E/tmp/trim.92A06229-3771-44B3-90C1-6408A0B297A2.MOV"

My code:

            do {
                let isSecureAccess = fileURL.startAccessingSecurityScopedResource()
                defer{fileURL.stopAccessingSecurityScopedResource()}
                let resourceValues = try fileURL.resourceValues(forKeys: [.fileSizeKey])
                // Gets the Mime Type
                let extUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileURL.pathExtension as CFString, nil)?.takeUnretainedValue()
                let mimeType:String = extUTI != nil ? (UTTypeCopyPreferredTagWithClass(extUTI!, kUTTagClassMIMEType)?.takeUnretainedValue() as String?) ?? defaultMimeType : defaultMimeType
                let  fileSize: Int = resourceValues.fileSize!
                blockBlob?.properties.length = NSNumber(value: fileSize)
                blockBlob?.properties.contentType  = mimeType
                blockBlob?.uploadFromFile(with: fileURL) { uploadError -> Void in
                    if uploadError == nil {
                        print("file has been uploaded")
                        let fileType = FileTypeUtils.getFileType(fileExtension: fileURL.pathExtension.lowercased())
                        let fileUri = FileTypeUtils.isMSOfficeFile(fileType: fileType) ? self.viewOfficePath + blockBlob!.storageUri.primaryUri.absoluteString : blockBlob!.storageUri.primaryUri.absoluteString
                        sharedFileDataModel = SharedFileDataModel(
                            fileURL.lastPathComponent,
                            fileUri,
                            fileType,
                            fileSize,
                            fileURL.path,
                            true
                        )
                    } else {
                        print(uploadError!)
                        self.delegate?.onFileUploadError(error: uploadError!)
                    }

                    self.delegate?.onFileUploaded(uploadedFile: sharedFileDataModel)
                }
            } catch {
                print(error)
                let userInfo = [NSLocalizedFailureReasonErrorKey: "General error in file upload"]
                let uploadError = NSError(domain: "BlobStorageService", code: -1, userInfo: userInfo)
                self.delegate?.onFileUploadError(error: uploadError)
            }

and defering the stopAccessingSecurityScopedResource() call , as you can see in our code.

Hmmm, that’s not what I’m taking away from the code snippet you posted. I believe that the uploadFromFile(with:) call is asynchronous, which is problematic because your defer block runs as you exit the do statement.

Add log statements to the start of the do statement, inside the defer block, and the start of the closure that you pass to uploadFromFile(with:). What order do they run in?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

ios app: Uploading video to Azure storage - error Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"
 
 
Q