What is the files and folders permission?

Hi there, I am currently making an app and trying to download content to a user-selected download directory (like how safari does it).

However, whenever I set the download directory outside the App's documents, I get the following error when running my code on a real iOS device.

downloadedFileMoveFailed(error: Error Domain=NSCocoaErrorDomain Code=513 "“CFNetworkDownload_dlIcno.tmp” couldn’t be moved because you don’t have permission to access “Downloads”." UserInfo={NSSourceFilePathErrorKey=/private/var/mobile/Containers/Data/Application/A24D885A-1306-4CE4-9B15-952AF92B7E6C/tmp/CFNetworkDownload_dlIcno.tmp, NSUserStringVariant=(Move), NSDestinationFilePath=/private/var/mobile/Containers/Shared/AppGroup/E6303CBC-62A3-4206-9C84-E37041894DEC/File Provider Storage/Downloads/100MB.bin, NSFilePath=/private/var/mobile/Containers/Data/Application/A24D885A-1306-4CE4-9B15-952AF92B7E6C/tmp/CFNetworkDownload_dlIcno.tmp, NSUnderlyingError=0x281d045d0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}, source: file:///private/var/mobile/Containers/Data/Application/A24D885A-1306-4CE4-9B15-952AF92B7E6C/tmp/CFNetworkDownload_dlIcno.tmp, destination: file:///private/var/mobile/Containers/Shared/AppGroup/E6303CBC-62A3-4206-9C84-E37041894DEC/File%20Provider%20Storage/Downloads/100MB.bin)

The summary of that error is that I don't have permission to access the folder I just granted access to.

Here's my attached code (I'm using AlamoFire since it's easier to understand, but I'm having a problem saving files in iOS):

import SwiftUI
import UniformTypeIdentifiers
import Alamofire

struct ContentView: View {
    @AppStorage("downloadsDirectory") var downloadsDirectory = ""
    
    @State private var showFileImporter = false
    
    var body: some View {
        VStack {
            Button("Set downloads directory") {
                showFileImporter.toggle()
            }
            
            Button("Save to downloads directory") {
                Task {
                    do {
                        let destination: DownloadRequest.Destination = { _, response in
                            let documentsURL = URL(string: downloadsDirectory)!
                            let suggestedName = response.suggestedFilename ?? "unknown"

                            let fileURL = documentsURL.appendingPathComponent(suggestedName)

                            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
                        }

                        let _ = try await AF.download(URL(string: "https://i.imgur.com/zaVQDFJ.png")!, to: destination).serializingDownloadedFileURL().value
                    } catch {
                        print("Downloading error!: \(error)")
                    }
                }
            }
        }
        .fileImporter(isPresented: $showFileImporter, allowedContentTypes: [UTType.folder]) { result in
            switch result {
            case .success(let url):
                downloadsDirectory = url.absoluteString
            case .failure(let error):
                print("Download picker error: \(error)")
            }
        }
    }
}

To reproduce (Run on a REAL iOS device!):

  1. Click the Set downloads directory button to On my iPhone
  2. Click the Save to downloads directory button
  3. Error occurs

Upon further investigation, I found that safari uses the Files and Folders privacy permission (Located in Settings > Privacy > Files and folders on an iPhone) to access folders outside the app sandbox (see the attached image for a visual representation of what I'm talking about). I scoured the web as much as I can and I couldn't find any documentation for this exact permission.

I have seen non-apple apps (such as VLC) use this permission, but I cannot figure out how it's granted. I tried enabling the following plist properties, but none of them work (because I later realized these are for macOS only)

<key>NSDocumentsFolderUsageDescription</key>
<string>App wants to access your documents folder</string>
<key>NSDownloadsFolderUsageDescription</key>
<string>App wants to access your downloads folder</string>

Can someone please help me figure out how to grant the files and folder permission and explain what it does? I would really appreciate the help.

Image of the permission in question:

There two potential issues here:

  • When you request a URL from the user, in this case via SwiftUI’s fileImporter(…), the URL you get back is security scoped. To work with the file system object referenced by that URL you have to start accessing it by calling startAccessingSecurityScopedResource() method, and then call stopAccessingSecurityScopedResource() when you’re done.

  • SwiftUI’s fileImporter(…) is intended to be used for read-only access. I don’t know if, when you request a directory, the resulting URL will give you read/write access to the directory. I’m not really an expert in this SwiftUI aspects of this, but I suspect that you’ll have more luck with fileExporter(…) or fileMover(…).

Share and Enjoy

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

**** you

/private/var/mobile/Containers/Shared/AppGroup/F9CF4E7C-1892-4BE1-8B7E-ADA70A9CF36D/avatarsCache/resized/2C48285D-BA7F-41BD-A598-CE7FA8BE7F91-36x36

What is the files and folders permission?
 
 
Q