Search code examples
objective-cccocoaappstore-sandbox

How to share a preferences file between two sandboxed applications?


In a project, I have an application and a helper running as an agent. The main application is used to set user preferences and can be unused for a while. The helper application that is launched by launchd at startup uses theses preferences to perform some daily tasks. It reads and updates the preferences file. The helper is located in "/Applications/Main.app/Contents/Library/LoginItems". When these applications were not sandboxed, it was easy to read and write the shared preferences file located in /Users/username/Library/Preferences/.

I've tried to add in the helper application the entitlement key com.apple.security.temporary-exception.shared-preference.read-write with an array containing the path of the main application preferences file : "/Library/Containers/com.mycompany.MainApp/Data/Library/Preferences/". But it simply doesn't work. For the helper application, the preferences file doesn't exist.


Solution

  • You'll need to utilise two things in order to support a shared container and preferences between a main and helper app:

    Security Application Groups

    In order for multiple apps to share a common container, you'll want to set the com.apple.security.application-groups entitlement (in your main and helper app) to a common identifier, such as @"com.company.my-app-suite". See Adding an App to a Group for more information.

    User Defaults Suites

    As per the Foundation Release Notes for OS X 10.9:

    For applications that are part of a Security Application Group, the NSUserDefaults "suite" APIs (-initWithSuiteName:, -addSuiteNamed: and -removeSuiteNamed:) will operate on a suite shared by applications in the group and stored in the group container, if the suite identifier is the identifier of the group.

    So you'll want to do something like this in your application delegate (or similar):

    - (NSUserDefaults *)sharedUserDefaults {
        static NSUserDefaults *shared = nil;
        if (!shared) {
            shared = [[NSUserDefaults alloc] initWithSuiteName:@"com.company.my-app-suite"];
        }
        return shared;
    }
    

    And use that instead of [NSUserDefaults standardUserDefaults] throughout both your apps.