swift – Cannot modify files outside my iOS app’s sandbox. Permissions issue


I’m developing an iOS app (latest Swift, iOS, Xcode versions atm of writing) which needs to rename audio files (and later modify its metadata) located anywhere (local storage, external storage, cloud, etc).

The trouble comes when testing on a physical device while trying to rename a file in some folder On My iPhone, the app does as intended when testing on the Simulator. On my physical iPhone I get an error logged saying that I don’t have the permissions to do that.

“filename.mp3” couldn’t be moved because you don’t have permission to access “randomFolder".

I did my googling and asked GPTs about it, learned about FileCoordinator, UIDocumentPickerViewController⁠, startAccessingSecurityScopedResource. I also saw this video, and finally followed this other one.

Some code I have in place:

This is the document picker which I then call from a .sheet on another View.

struct DocumentPicker: UIViewControllerRepresentable {
    @Binding var newName: String
    @EnvironmentObject private var bookmarkController: BookmarkController
    
    func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
        let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: supportedTypes)
        documentPicker.delegate = context.coordinator
        return documentPicker
    }
    
    func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {
        print("updateUIViewController documentPicker")
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self, newName)
    }

    class Coordinator: NSObject, UIDocumentPickerDelegate {
        var parent: DocumentPicker
        var newName: String
        
        init(_ parent: DocumentPicker, _ newName: String = "") {
            self.parent = parent
            self.newName = newName
        }
        
        func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
            // save bookmark
            print("documentPicker \(urls[0])")
            parent.bookmarkController.addBookmark(for: urls[0])
            
            // Rename the file
            var error: NSError?
            
            NSFileCoordinator().coordinate(readingItemAt: urls[0], options: [], error: &error) { coordinatedURL in
                do {
                    //                let data = try Data(contentsOf: newURL)
                    print("urls[0]: \(urls[0])")
                    print("coordinatedURL: \(coordinatedURL)")
                    print("renamedURL: \(newName)")
                    try renameFile(at: coordinatedURL, to: newName)
} catch  {
                    print("Error: \(error.localizedDescription)")
                }   
            }
        }
    }
}

Renaming function:


/// Rename selected file from browser
func renameFile(at fileURL: URL, to newName: String) throws {
    let fileExtension = fileURL.pathExtension
    let directory = fileURL.deletingLastPathComponent()

    // Create a new URL with the updated name and the original extension
    let renamedURL = directory.appendingPathComponent(newName).appendingPathExtension(fileExtension)

    try FileManager.default.moveItem(at: fileURL, to: renamedURL)
}

I have a BookmarkController in place identical to the one in the 2nd video I mentioned above so that my URLs are bookmarked for later use. Here it is:

import SwiftUI
import MobileCoreServices

class BookmarkController: ObservableObject {
    @Published var urls: [URL] = []
    
    init() {
        loadAllBookmarks()
    }
    
    func addBookmark(for url: URL) {
        print("adding bookmark for \(url)")
        do {
            guard url.startAccessingSecurityScopedResource() else {
                print("Failed to obtain access to the security-scoped resource.")
                return
            }
            
            defer { url.stopAccessingSecurityScopedResource() }
            
            let bookmarkData = try url.bookmarkData(options: .minimalBookmark, includingResourceValuesForKeys: nil)
            
            let uuid = UUID().uuidString
            try bookmarkData.write(to: getAppSandboxDir().appendingPathComponent(uuid))
            
            urls.append(url)
        } catch {
            print("Error Adding Bookmark: \(error.localizedDescription)")
        }
    }
    
    func loadAllBookmarks() {
        // Get all the bookmark files
        let files = try? FileManager.default.contentsOfDirectory(at: getAppSandboxDir(), includingPropertiesForKeys: nil)
        // Map over the bookmark files
        self.urls = files?.compactMap { file in
            do {
                let bookmarkData = try Data(contentsOf: file)
                var isStale = false
                // Get the URL from each bookmark
                let url = try URL(resolvingBookmarkData: bookmarkData, bookmarkDataIsStale: &isStale)
                
                guard !isStale else {
                    // Handle stale bookmarks
                    return nil
                }
                print("loaded bookmark: \(url)")
                // Return URL
                return url
            } catch {
                print("Error Loading Bookmark: \(error.localizedDescription)")
                return nil
            }
        } ?? []
    }
    
    private func getAppSandboxDir() -> URL {
        // TODO ver 0 index
        FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    }
}

When running on my device, I see url.startAccessingSecurityScopedResource throwing:

Failed to obtain access to the security-scoped resource.

I have also tried getting access to the parent directory like this:

let parentURL = url.deletingLastPathComponent()

and then using parentURL instead, but it also fails.


Something I noticed is that my app is not shown in the Settings -> Privacy & Security -> Files and Folders list. My app didn’t trigger any dialog to be added here given user consent.

Fiddling with entitlements in Info.plist and Capabilities did not help either, but I’m not so sure about which ones should I add.

So:

Could it be that the problem I’m seeing about permissions and my app not appearing at the Files and Folders privacy settings on my iPhone are because my app is signed with a developer account which is not in the apple developer program? this is my last thought since I’m not understanding what else should I try.

Any pointers and help will be much appreciated.
Thanks!

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img