There isn’t one right answer to this problem, but here’s how I’d approach it:
Both the daemon and viewer application uses the same core data store to save and retrieve data.
Because sharing a Core Data store between processes isn’t supported (as far as I know), I’d have the daemon expose an XPC Service. Instead of opening the Core Data store itself, the viewer would use an NSXPCConnection to access the data via the daemon.
Assuming the viewer never runs without the daemon, it can use SMLoginItemSetEnabled, like you mentioned in the question, to register a mach service for the daemon, and then connect to that service.
There’s sample code that goes over the details of setting that up here on Apple’s website (summary: the daemon needs to be at App.app/Contents/Library/LoginItems/daemon.bundle.id.app), and you might also want to read this blog post that discusses some extra requirements imposed by sandboxing (summary: make extra sure that your Team ID is in the daemon’s bundle identifier).
The daemon should always start when the viewer starts.
All set: once you register the daemon with SMLoginItemSetEnabled, launchd will start it (if necessary) when the viewer connects to its XPC Service.
Now when the user quits the daemon, the viewer should also stop.
The viewer can use the NSXPCConnection to find out when the daemon quits. The daemon can also use SMLoginItemSetEnabled to un-register itself before it quits, so that it doesn’t get relaunched.
I would like to use
NSUserDefaultsfor basic configuration, can I somehow interchange this data between the daemon and the viewer? Again, both applications will have different bundle identifiers.
Use a suite for this:
// To read or write:
NSUserDefaults* suiteDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.example.app.shared"];
[suiteDefaults setObject:[NSDate date] forKey:@"launchTime"];
// Add the suite to -standardUserDefaults to make reading easier:
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults addSuiteNamed:@"com.example.app.shared"];
To work with sandboxing, the viewer and the daemon must share an App Group. You can even use KVO to observe changes to shared keys.