29

My Mac app is sandboxed and I need to save a file. Where do I save this file? I can't seem to find the specific place where this is allowed without using an open panel. This is how I do it on iOS:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];

What is the equivalent for the sandboxed directory on Mac?

Jack Humphries
  • 13,056
  • 14
  • 84
  • 125

4 Answers4

30

That code snippet works on the Mac regardless of whether your application is sandboxed.

In a non-sandboxed Mac app, path will refer to the Documents folder in your home: e.g. /Users/username/Documents.

In a sandboxed app, it refers to a folder within the app sandbox: e.g. /Users/username/Library/Containers/com.yourcompany.YourApp/Documents

See the docs for details.

rickster
  • 124,678
  • 26
  • 272
  • 326
11

Apple's Sandboxing guide is very useful, found here.

You basically have a folder dedicated for your app, as described by theAmateurProgrammer in reply to my question here.

~/Library/Container/com.yourcompany.yourappname/

Here is what I have so far, I will improve it later:

//Create App directory if not exists:
NSFileManager* fileManager = [[NSFileManager alloc] init];
NSString* bundleID = [[NSBundle mainBundle] bundleIdentifier];
NSArray* urlPaths = [fileManager URLsForDirectory:NSApplicationSupportDirectory 
                                        inDomains:NSUserDomainMask];

NSURL* appDirectory = [[urlPaths objectAtIndex:0] URLByAppendingPathComponent:bundleID isDirectory:YES];

//TODO: handle the error
if (![fileManager fileExistsAtPath:[appDirectory path]]) {
    [fileManager createDirectoryAtURL:appDirectory withIntermediateDirectories:NO attributes:nil error:nil];
}
Community
  • 1
  • 1
Mazyod
  • 22,319
  • 10
  • 92
  • 157
5

Converting @Mazyod's answer into Swift (5.1):

var appPath: URL? {
    //Create App directory if not exists:
    let fileManager = FileManager()
    let urlPaths = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask)
    if let bundleID = Bundle.main.bundleIdentifier, let appDirectory = urlPaths.first?.appendingPathComponent(bundleID,isDirectory: true) {
        var objCTrue: ObjCBool = true
        let path = appDirectory.path
        if !fileManager.fileExists(atPath: path,isDirectory: &objCTrue) {
            do {
                try fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
            } catch {
                return nil
            }
        }
        return appDirectory
    }
    return nil
}

However, the directory has changed and I am not sure that the additonal repetition of the bundle ID is needed as the path is

"/Users/**user name**/Library/Containers/**bundleID**/Data/Library/Application Support/**bundleID**". 

But it seems to work.

Wizard of Kneup
  • 1,863
  • 1
  • 18
  • 35
1

Is is even easier. For sandboxed apps on macOS the function NSHomeDirectory gives you the path where you have read and write access and can save all your files. It will be a path like this

/Users/username/Library/Containers/com.yourcompany.YourApp
FLUXparticle
  • 733
  • 6
  • 16