Security scoped Bookmarks iOS - Swift

Hey, I am trying to save the url which I get from my didPickDocumentAt as a security scoped bookmark

This is my code:

Here just the declaration of the UIDocumentPickerViewController

let documentPicker = UIDocumentPickerViewController(documentTypes: [kUTTypePlainText as String], in: .open)

documentPicker.delegate = self
if #available(iOS 11.0, *)
{
     documentPicker.allowsMultipleSelection = false
}
else
{
    // Fallback on earlier versions
}
present(documentPicker, animated: true, completion: nil)


Now didPickDocumentAt - there I am saving my bookmark data from my selected file on the smb server. Data.bookmarkAdmin is just the variable in which I store the bookmark.

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL)
{
    print(url)
        
    do
    {
        let shouldStoppAccessing = url.startAccessingSecurityScopedResource()
        defer
        {
            if shouldStoppAccessing
            {
                url.stopAccessingSecurityScopedResource()
            }
        }

        let bookmarkData: Foundation.Data = try url.bookmarkData()
            
        Data.bookmarkAdmin = bookmarkData
        print(Data.bookmarkAdmin)
    }
    catch let error
    {
        print(error)
    }
}


Later in my app I want to use the bookmark data to create the url again and write on that file

func readFile()
{
    Admin.fileContent = ""
    Admin.readString.removeAll()
        
    do
    {
        var isStale = false
        
        let url = try URL(resolvingBookmarkData: Data.bookmarkAdmin, bookmarkDataIsStale: &isStale)

        guard !isStale
        else
        {
            return
        }
        
        var errorText: Error?
                do
                {
                    let didStartAccessing = url.startAccessingSecurityScopedResource()
                    defer
                    {
                        if didStartAccessing
                        {
                            url.stopAccessingSecurityScopedResource()
                        }
                    }

                    print(url)
                    let fileURLs =  try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil)
                        Admin.fileContent = try String(contentsOf: fileURLs[0], encoding: .utf8)
                        print("ADMIN: \(Admin.fileContent)")
                    }
                    catch
                    {
                        errorText = error
                    }
                    
            //        Errorhandling
                    if errorText != nil
                    {
                        let alertController = UIAlertController(title: "Error", message: "Keine Datei gefunden \(String(describing: errorText))", preferredStyle: .alert)
                        
                        let okAction = UIAlertAction(title: "ok", style: .default, handler: nil)
                        
                        alertController.addAction(okAction)
                        
                        present(alertController, animated: true, completion: nil)
                    }
                    else {}
        }
    catch let error {}
}

I get an error in my readFile() function ERROR:

Error Domain=NSCocoaErrorDomain Code=256 "The file “testaufbau” couldn’t be opened." UserInfo={NSURL=file:///private/var/mobile/Library/LiveFiles/com.apple.filesystems.smbclientd/ctPrTwTestApp/testaufbau.csv, NSFilePath=/private/var/mobile/Library/LiveFiles/com.apple.filesystems.smbclientd/ctPrTwTestApp/testaufbau.csv, NSUnderlyingError=0x2834bfbd0 {Error Domain=NSPOSIXErrorDomain Code=20 "Not a directory"}}

When I set the url manually in the readFile() without the bookmark just

let url: URL = URL(fileURLWithPath: "/private/var/mobile/Library/LiveFiles/com.apple.filesystems.smbclientd/ctPrTwTestApp/testaufbau.csv")

Then I get a different ERROR:

Error Domain=NSCocoaErrorDomain Code=257 "The file “testaufbau.csv” couldn’t be opened because you don’t have permission to view it." UserInfo={NSURL=file:///private/var/mobile/Library/LiveFiles/com.apple.filesystems.smbclientd/ctPrTwTestApp/testaufbau.csv, NSFilePath=/private/var/mobile/Library/LiveFiles/com.apple.filesystems.smbclientd/ctPrTwTestApp/testaufbau.csv, NSUnderlyingError=0x280ac1440 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}

How can I solve this and how do I get permission?

Accepted Reply

Look at the doc.


It exists, but you have to specify options:

init(contentsOf: URL, options: Data.ReadingOptions)


That's for smb access ? Didn't eskimo answer the question here: https://forums.developer.apple.com/message/415231#415231

Replies

I do not see in your code (may be I did not search enough) where you save the bookmark.

Second code describiton line 16 - 18. If this is the wrong way please show me the right one.

Sorry I cannot help more. I'm not sure those lines save the bookmark in a persistent way.


Hope this will lead you to the solution:

https://developer.apple.com/documentation/uikit/view_controllers/providing_access_to_directories

Could you explain me what they call when there is written

getMyURLForBookmark()

What is written behind this. It is never explained.

where do they write it to?

You're right, this document is not self content.


For what I've implemented in a MacOS context, should be close to this :


func bookmarkPath() -> String {
    var url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL
    url = url.appendingPathComponent("Bookmarks.dict")
    return url.path
}

or, if I try to adapt (but did not test, take care)


func getMyURLForBookmark() -> URL {
    var url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL     // That's for MacOS, check for iOS
    url = url.appendingPathComponent("Bookmarks.dict")
    return url
}


Good luck, as sandboxing is really full of pitfalls and not enough documented.

I am doing something wrong or it is not working.

And I just realized that they: https://developer.apple.com/documentation/uikit/view_controllers/providing_access_to_directories

call when reading the Bookmark

let bookmarkData = try Data(contentsOf: getMyURLForBookmark())

But this doesn't exists. I always get the error

Argument passed to call that takes no arguments

Only NSData has

NSData(contentsOf: URL)

If I want to use this and than transform it back to Data() I get the error

Cannot convert value of type 'appName.Data' to expected argument type 'Foundation.Data'


I just want to access a smb server in the files app or doesn't matter how. This can't be that difficult. Mhhh 😟

Look at the doc.


It exists, but you have to specify options:

init(contentsOf: URL, options: Data.ReadingOptions)


That's for smb access ? Didn't eskimo answer the question here: https://forums.developer.apple.com/message/415231#415231

I get an error in my readFile() function ERROR:

You should better write the line where the error occurred.

I guess it's in line 31. of your third code:

                let fileURLs =  try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil)

The method `default.contentsOfDirectory(at:includingPropertiesForKeys:)` works for a url representing a directory, not for a file.


Second, in the doc you referenced in your old thread:

ArticleProviding Access to Directories

it says

// Use file coordination for reading and writing any of the URL’s content.

but you are not using `

NSFileCoordinator`
in your current code.

Didn't eskimo answer the question here:

Yep. I’m avoiding this thread just to stay focused.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I finally figured it out. Now everything is working fine, but thank you for your help