One reason to sandbox is that it means you're ready to go if you decide someday to submit your app to the App Store. For example, all of Microsoft's enormous Office apps for Mac are sandboxed, yet they're not distributed via the App Store. Clearly Microsoft is prepared if they someday choose to go that route.
A less obvious reason is that going through the process of sandboxing can result in a better app, even if you won't be submitting it to the App Store, or even if you don't ultimately distribute the app sandboxed.
Sandboxing forces you to go through an inventory of just what files, resources and services your app requires. You then request only those things and nothing more. Sandboxing's point of the view is that of the user, not you. After all, it's the user's computer, not yours, and the user is allowing your app to execute on their computer and access their files and resources. Limiting your app to only what it needs and accessing those things properly is one way of honoring the user's point of view. (Plus, users are less and less tolerant of apps that are distributed and implemented like in the old days: zipped, unsigned, doing whatever they want.)
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
</dict>
</plist>
To check the syntax of your entitlements file, use the plutil utility:
plutil myapp-entitlements.plist
Now add the entitlements file to the script that you use to codesign your
app. The script should look something like this (substitute your name and ID):
codesign --force --deep --verbose --sign "Developer ID Application: Your Name (Your ID)" --entitlements myapp-entitlements.plist "My App.app"
After running the script, check that everything went okay with codesigning
by entering this:
spctl --assess --verbose --type execute "My App.app"
Now start your app and see what no longer works.
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
Now the commands should work.
This system does not appear to work with Lazarus apps compiled with the Cocoa widgetset. With Lazarus, everything is done manually, from creating the main menu to opening files. This probably interferes with the normal Cocoa system of opening and managing recent files.
You can create and manage your own Open Recent submenu, but when your sandboxed app tries to open a recent file (from a previous session), it gets blocked because macOS has no way of knowing that the file was selected originally by the user (meaning it's okay to open). It just thinks you're attempting to open an arbitrary file, which is not allowed. Cocoa has a system for keeping track of recently opened files, it's just that Lazarus apps don't seem to be able to utilize it.
/var/folders/x4/s130kfgx3fngc3w9rg5v8gr80000gn/T/
In a sandboxed app, this path changes automatically to include the
bundle ID of your app. For example:
/var/folders/x4/s130kfgx3fngc3w9rg5v8gr80000gn/T/com.mydomain.myapp
As long as you're using those functions consistently throughout your code,
you shouldn't have to change anything for a sandboxed app.
You can think of the app's container folder as a kind of "private" home folder. It has its own Library and Documents folders, for example, so if you're already using those locations to store files such as databases that persist across runs of your app, you may not have to make any changes to your code.
If you're using the Foundation framework's NSHomeDirectory function, when your app is sandboxed this function will point to the container folder instead of the user's normal home folder.
In a sandboxed app, preferences are stored in the Library/Preferences folder in your app's container. If you're using NSUserDefaults, you don't have to make any changes.
See the NSMisc.pas unit from here for examples of how to use NSUserDefaults.
<key>com.apple.security.network.client</key>
<true/>
Note that OpenSSL-based HTTP clients are not supported in sandboxed apps.
This includes Indy, Synapse and FPC's HTTP client. OpenSSL on Mac has been
deprecated
for a long time, so you should be using one of Apple's APIs instead.If you need to make GET or POST requests, you can use the ns_url_request.pas unit from here. It uses the Foundation framework's NSURLConnection class.
<key>com.apple.security.print</key>
<true/>
<key>com.apple.security.scripting-targets</key>
<dict>
<key>com.apple.mail</key>
<array>
<string>com.apple.mail.compose</string>
</array>
</dict>
You can determine the names of any access groups by looking in the app's
.sdef file (normally in the app's Resources folder).
<key>com.apple.security.temporary-exception.apple-events</key>
<string>com.microsoft.Excel</string>
codesign --force --verbose --sign "Developer ID Application: Your Name (Your ID)" MyApp.dmg
spctl --assess --verbose --type install MyApp.dmg
macpgmr (at) icloud (dot) com
First posted Feb. 25, 2018; last edited Feb. 25, 2018.
Code syntax highlighting done with highlight.js.