Sandboxed Launch Services
As far as I can tell, new restrictions have been placed on sandboxed apps in 10.12 that prevent such apps from using a variety of LaunchServices functions (or their NSWorkspace abstractions) that would reveal the path on disk of a specific other application.
For example, if I have BBEdit installed in ~/Applications, a sandboxed MarsEdit cannot tell that it exists. This is even though MarsEdit has an entitlement to send Apple events to BBEdit, and it can open documents in BBEdit, and BBEdit may already be running. With previous OS versions, NSWorkspace
would let MarsEdit look up BBEdit by its bundle identifier, even though MarsEdit might not have access to read the returned URL. On Sierra, unless BBEdit is in the global /Applications folder, or another that sandboxed apps can read, NSWorkspace
returns nil
(and no error) as if BBEdit didn’t exist. Apparently, returning the inaccessible but nonetheless useful URL is considered a bug.
This is a bit surprising because we know that Launch Services is not actually scanning for applications from within the sandboxed app. Rather, it’s returning cached data that was gathered by a process with greater privileges. However, it turns out that that process verifies the privileges in the context of the application. The application only receives URLs that it can read.
My takeaways from this long Twitter conversation are:
- The system is no longer really designed to support applications that are not stored in /Applications. Other locations require more coding and testing and are subject to changing OS behavior.
- The Launch Services restrictions make sense, technically, but they add up to an inconsistent and ultimately user-hostile experience.
- There are legitimate questions that applications want answered, but cannot answer for themselves (when sandboxed). The system should provide a safe way for applications to get answers to these questions.
- It’s unfortunate that these types of sandbox behaviors are still not documented.
Lastly, since this API change was not documented, I was thinking about how a developer might have detected it, other than through a bug report such as mine to Jalkut. The natural answer is unit tests. I have a lot of tests of basic OS behavior that can help detect changes and regressions. Unfortunately, as far as I know, Xcode can’t run unit tests inside the sandbox. So code that works as expected under test may not work as expected in an actual app.