Read and write permission for user selected folder in Mac OS app?

13,873

Solution 1

Add user-selected and bookmarks.app permissions in entitlement file :

<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>

Then open folder selection using NSOpenPanel so the user can select which folders to give you access to. The NSOpenPanel must be stored as a bookmark and saved to disk. Then your app will have the same level of access as it did when the user selected the folder.

Solution 2

Here is my Answer How to do implement and persist Read and write permission of user selected folder in Mac OS app?

GitHub Example Project link

First :

Add user-selected and bookmarks.app permissions in entitlement file :

<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>

Then i created class for all bookmark related function required for storeing, loading ... etc bookmarks app.

import Foundation
import Cocoa

var bookmarks = [URL: Data]()

func openFolderSelection() -> URL?
{
    let openPanel = NSOpenPanel()
    openPanel.allowsMultipleSelection = false
    openPanel.canChooseDirectories = true
    openPanel.canCreateDirectories = true
    openPanel.canChooseFiles = false
    openPanel.begin
        { (result) -> Void in
            if result.rawValue == NSApplication.ModalResponse.OK.rawValue
            {
                let url = openPanel.url
                storeFolderInBookmark(url: url!)
            }
    }
    return openPanel.url
}

func saveBookmarksData()
{
    let path = getBookmarkPath()
    NSKeyedArchiver.archiveRootObject(bookmarks, toFile: path)
}

func storeFolderInBookmark(url: URL)
{
    do
    {
        let data = try url.bookmarkData(options: NSURL.BookmarkCreationOptions.withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil)
        bookmarks[url] = data
    }
    catch
    {
        Swift.print ("Error storing bookmarks")
    }

}

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

func loadBookmarks()
{
    let path = getBookmarkPath()
    bookmarks = NSKeyedUnarchiver.unarchiveObject(withFile: path) as! [URL: Data]
    for bookmark in bookmarks
    {
        restoreBookmark(bookmark)
    }
}



func restoreBookmark(_ bookmark: (key: URL, value: Data))
{
    let restoredUrl: URL?
    var isStale = false

    Swift.print ("Restoring \(bookmark.key)")
    do
    {
        restoredUrl = try URL.init(resolvingBookmarkData: bookmark.value, options: NSURL.BookmarkResolutionOptions.withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
    }
    catch
    {
        Swift.print ("Error restoring bookmarks")
        restoredUrl = nil
    }

    if let url = restoredUrl
    {
        if isStale
        {
            Swift.print ("URL is stale")
        }
        else
        {
            if !url.startAccessingSecurityScopedResource()
            {
                Swift.print ("Couldn't access: \(url.path)")
            }
        }
    }

}

Then open folder selection using NSOpenPanel so the user can select which folders to give you access to. The NSOpenPanel must be stored as a bookmark and saved to disk. Then your app will have the same level of access as it did when the user selected the folder.

To open NSOpenPanel :

let selectedURL = openFolderSelection()
saveBookmarksData()

and to load existing bookmark after app close :

loadBookmarks()

Thats it. I Hope it will help someone.

Share:
13,873
Sid Mhatre
Author by

Sid Mhatre

working as iOS app developer and data analytics

Updated on July 22, 2022

Comments

  • Sid Mhatre
    Sid Mhatre almost 2 years

    I am developing MAC OS app which have functionality to create file on the behalf of your. First user select folder for storing file (One time at start of app) and then user can select type and name of the file user want to create on selected folder (Folder selected on start of the app) using apple script. I am able to create file when i add below temporary-exception in entitlement file but its not able to app apple review team but works in sandboxing.

    Guideline 2.4.5(i) - Performance We've determined that one or more temporary entitlement exceptions requested for this app are not appropriate and will not be granted:

    com.apple.security.temporary-exception.files.home-relative-path.read-write
    /FolderName/
    

    I found :

    Enabling App Sandbox - Allows apps to write executable files.

    And

    Enabling User-Selected File Access - Xcode provides a pop-up menu, in the Summary tab of the target editor, with choices to enable read-only or read/write access to files and folders that the user explicitly selects. When you enable user-selected file access, you gain programmatic access to files and folders that the user opens using an NSOpenPanel object, and files the user saves using an NSSavePanel object.

    Using below code for creating file :

    let str = "Super long string here"
    let filename = getDocumentsDirectory().appendingPathComponent("/xyz/output.txt")
    
    do {
        try str.write(to: filename, atomically: true, encoding: String.Encoding.utf8)
    } catch {
        // failed to write file – bad permissions, bad filename, missing permissions, or more likely it can't be converted to the encoding
    }
    

    Also tried adding com.apple.security.files.user-selected.read-write in entitlement file for an NSOpenPanel object :

    <key>com.apple.security.files.user-selected.read-write</key>
    <true/>
    

    Is there any way to get pass apple review team to approve Mac App with read and write permission to user selected folder ?

  • Sid Mhatre
    Sid Mhatre over 6 years
    Thanks for the response i will try this.
  • Parwat Kunwar
    Parwat Kunwar over 5 years
    Can we define the custom folder read/write access apart from user-selected, movies, pictures etc. OK likeDocuments
  • Andy
    Andy almost 5 years
    Looks interesting Sid but I cannot compile your GitHub project because it crashes in func loadBookmarks().
  • Wizard of Kneup
    Wizard of Kneup over 4 years
    Andy, you are right. I edited the code in order to fix that issue. Basically, if bookmarks were never saved then you cannot load them and force unwrap.